Laravel:在多个值上过滤多对多

时间:2015-01-26 23:22:30

标签: php database laravel eloquent

我为我的laravel应用程序构建了一个分层导航模块 - 就像在Magento或WooCommerce中完成的那样。这是一个想法:可以为产品分配单个或多个属性,然后用户应该能够使用这些属性来过滤产品。就像属性"材料"可以为产品分配一个或多个值,例如 iron wood & 塑料。我的问题是我无法弄清楚如何正确地做到这一点。

我的数据模型是这样的:

         products table:  id | name         | other info...
                example:  42 | Chair        | ...
                example:  14 | Bike         | ...

       attributes table:  id | name         | description
                example:  02 | Material     | the material that the...

attribute_options table:  id | attribute_id | value    
                example:  11 | 02           | Wood    
                example:  12 | 02           | Iron    

            pivot table:  id | product_id   | attribute_option_id    
                example:  12 | 42           | 11    
                example:  12 | 42           | 12    
                example:  12 | 14           | 12    

我在产品产品选项模型(因此是数据透视表)之间设置了多对多关系。

在我的视图文件中,会显示一个表单,该表单会循环显示所有不同的属性,然后循环显示其选项,并为每个选项创建一个复选框,其值为id' s 。理想情况下,这些值在一个数组中组合在一起,并命名如下: filter [attribute_id] [attribute_option_id] 。一个例子:

<input type="checkbox" name="filter[02][11]" value="id"> Wood
<input type="checkbox" name="filter[02][12]" value="id"> Iron
<input type="checkbox" name="filter[xx][xx]" value="id"> Etc..

在提交表单时,所有选定的属性选项值都会发送到服务器,以便处理此信息,并且只返回符合所有不同条件的产品。

因此,如果过滤了[02] [11]和[02] [12]过滤器,则只返回具有&#34; Wood&#34;属性选项的产品。和&#34;铁&#34;分配

起初,我认为这很简单,但我认为我不像我想的那样熟练......因为这是一个Laravel问题,我喜欢雄辩风格解决方案!

P.S。如果我搞砸了(思考我的)数据模型,请告诉我!我还在学习很多关于Web开发的知识,也许我的问题有更好/更清晰的解决方案/方法

--------编辑/补充资料--------

使用以下代码我可以过滤一个属性

$products = $products->whereHas('attributeOptions', function($query)
{
    $query->where(function($query)
    {
        $query->where('value', 'iron');
    });
});

但是由于产品可以具有不同类型的多个属性(如颜色和材质或多种材质),我需要能够设置多个这样的属性:

$products = $products->whereHas('attributeOptions', function($query)
{
    $query->where(function($query)
    {
        $query->where('value', 'iron');
        $query->where('value', 'wood');
        $query->where('value', 'yellow');
    });
});

但是这段代码不起作用,因为它检查一行有多个值,而不是检查具有相同product_id的多行的值。

产品 attribute_options 之间的关系为:

public function attributeOptions() {

    return $this->belongsToMany('AttributeOption', 'products_attribute_options', 'product_id', 'attribute_option_id');

}

这在Eloquent / Laravel中是否可行?或者我是否需要不同的解决方案

提前致谢,我很乐意向您学习!

2 个答案:

答案 0 :(得分:3)

我会通过使用whereHas方法,然后使用whereIn来隔离包含所需属性的记录,然后将其过滤到只有具有正确数量的属性的记录。

// Assuming you have the IDs of the attributes, rather than the value
$desiredAttributes = [11,12];
$products = $products->whereHas('attributeOptions', function($query)     
use($desiredAttributes)
{
    $query->whereIn('attribute_id', $desiredAttributes);
}, "=", count($desiredAttributes))->get();

所以这将首先获取具有属性11或12的所有记录,然后过滤到只有具有计数的记录(attribute_option_id)== 2

答案 1 :(得分:2)

我完全同意@astro和@Dave的评论。您应该重新考虑您的数据库设计。我将在这里重新发布@Dave的两个链接,以便他们获得更多的可见性:

How to implement filter system in SQL?

What is best performance for Retrieving MySQL EAV results as Relational Table


虽然我不认为这在性能方面表现良好(有许多属性和产品),但这里的解决方案应该适用于您当前的设置:

将您的复选框更改为:

<input type="checkbox" name="filter[02][]" value="11"> Wood
<input type="checkbox" name="filter[02][]" value="12"> Iron
<input type="checkbox" name="filter[xx][]" value="xx"> Etc..

这样,同一过滤器的多个值将作为数值数组添加到filter[02][]

$query = Product::query();
foreach(Input::get('filter') as $attributeId => $optionIds){
    foreach($optionIds as $optionId){
        $query->whereHas('attributeOptions', function($q) use ($attributeId, $optionId){
            $q->where('attributeId', $attributeId)
              ->where('attribute_option_id', $optionId);
        }
    }
}
$products = $query->get();
相关问题