Laravel:对多个值进行多对多过滤
Laravel: Filter many to many on multiple values
我正在为我的 laravel 应用程序构建分层导航模块 - 与在 Magento 或 WooCommerce 中所做的非常相似。这个想法是:可以为产品分配一个或多个属性,然后用户应该能够使用这些属性筛选产品。就像属性 "material" 一样,可以为产品分配一个或多个值,例如 iron、wood & plastic。我的问题是我不知道如何正确地做到这一点。
我的数据模型是这样的:
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
我在 Product 和 Product Option 模型之间设置了多对多关系(因此枢轴 table ).
在我的视图文件中,显示了一个表单,它循环遍历所有不同的属性,然后循环遍历它们的选项,并为每个选项创建一个复选框,其 id 的值为.这些值理想地组合在一个数组中,并像这样命名: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],则只应返回分配了 "Wood" 和 "Iron" 属性选项的产品。
起初,我认为这会很简单,但我认为我没有我想象的那么熟练...因为这是一个 Laravel 问题,我很乐意eloquent 风格的解决方案!
P.S。如果我搞砸了(在我的背后思考)数据模型,请告诉我!我仍在学习很多关于 Web 开发的知识,也许 better/cleaner solution/approach 我的问题
--------编辑/附加信息--------
使用以下代码我可以过滤一个属性
$products = $products->whereHas('attributeOptions', function($query)
{
$query->where(function($query)
{
$query->where('value', 'iron');
});
});
但由于产品可以具有多个不同类型的属性(如颜色和 material 或多个 material),我需要能够像这样设置多个位置:
$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 中是否可行,或者我需要不同的解决方案
在此先感谢,我很乐意向您学习!
我会通过使用 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 的记录,然后过滤到仅具有 count(attribute_option_id) == 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();
我正在为我的 laravel 应用程序构建分层导航模块 - 与在 Magento 或 WooCommerce 中所做的非常相似。这个想法是:可以为产品分配一个或多个属性,然后用户应该能够使用这些属性筛选产品。就像属性 "material" 一样,可以为产品分配一个或多个值,例如 iron、wood & plastic。我的问题是我不知道如何正确地做到这一点。
我的数据模型是这样的:
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
我在 Product 和 Product Option 模型之间设置了多对多关系(因此枢轴 table ).
在我的视图文件中,显示了一个表单,它循环遍历所有不同的属性,然后循环遍历它们的选项,并为每个选项创建一个复选框,其 id 的值为.这些值理想地组合在一个数组中,并像这样命名: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],则只应返回分配了 "Wood" 和 "Iron" 属性选项的产品。
起初,我认为这会很简单,但我认为我没有我想象的那么熟练...因为这是一个 Laravel 问题,我很乐意eloquent 风格的解决方案!
P.S。如果我搞砸了(在我的背后思考)数据模型,请告诉我!我仍在学习很多关于 Web 开发的知识,也许 better/cleaner solution/approach 我的问题
--------编辑/附加信息--------
使用以下代码我可以过滤一个属性
$products = $products->whereHas('attributeOptions', function($query)
{
$query->where(function($query)
{
$query->where('value', 'iron');
});
});
但由于产品可以具有多个不同类型的属性(如颜色和 material 或多个 material),我需要能够像这样设置多个位置:
$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 中是否可行,或者我需要不同的解决方案
在此先感谢,我很乐意向您学习!
我会通过使用 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 的记录,然后过滤到仅具有 count(attribute_option_id) == 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();