我有一个Model
关系的manyToMany
:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
public function categories()
{
return $this->belongsToMany(Category::class, 'product_category', 'product_id', 'category_id');
}
}
因此1个Product
可以有多个Categories
。
我正在尝试查询(提取)所有与类别完全匹配的Products
:
示例:
$categories = ['category1', 'category2'];
Product::whereHas('categories', function($q) use ($categories){
foreach ($categories as $categoryName) {
$q->where('name', $categoryName);
}
//or $q->whereIn('name', $categories);
})->get();
也尝试过:
$categories = ['category1', 'category2'];
Product::whereHas('categories', function($q) use ($categories){
$q->whereIn('name', $categories);
}, '=', count($categories))->get();
假设我的Product
仅附加了category1
,则查询应返回此产品。
如果Product
同时具有categories
,但是我的数组仅包含category1
,则应忽略此Product
。
所以我正在尝试实现:仅提取具有特定类别的products
。可以使用Eloquent
或DB
构建器吗?
伪代码
$allow = for each Product->categories{
category Exist in $categories
}
答案 0 :(得分:1)
根据您的要求,您的MySQL查询如下:
LEFT JOIN
获取产品映射的categories
的总数。GROUP BY
来比较其total mapped categories
与matching categories based on user input
。HAVING
子句完成,其中total mapped categories
应该等于用户提供的类别计数。相同的matching categories based on user input
还应该与用户提供的类别的确切数量相匹配。SELECT p.id
FROM products p
LEFT JOIN product_category pc ON p.id = pc.product_id
LEFT JOIN categories c ON c.id = pc.category_id AND c.name IN ('category1', 'category2')
GROUP BY p.id
HAVING COUNT(0) = 2 -- To match that product should have only two categories
AND SUM(IF(c.name IN ('category1', 'category2'), 1, 0)) = 2; -- To match that product have only those two categories which user has provided.
相同的查询构建器可以通过以下方式实现:
$arrCategory = ['category1', 'category2'];
$strCategory = "'" . implode("','", $arrCategory) . "'";
$intLength = count($arrCategory);
$products = Product::leftJOin('product_category', 'products.id', '=', 'product_category.product_id')
->leftJoin('categories', function ($join) use ($arrCategory) {
$join->on('categories.id', '=', 'product_category.category_id')
->whereIn('categories.name', $arrCategory);
})
->groupBy('products.id')
->havingRaw("COUNT(0) = $intLength AND SUM(IF(categories.name IN ($strCategory), 1, 0)) = $intLength")
->select(['products.id'])
->get();
dd($products);
注意:如果在MySQL中启用了only_full_group_by
模式,则在要获取的group by
和select
子句中都包含产品表的列。
答案 1 :(得分:0)
尝试在没有地方的情况下使用此
Product::with(array('categories' => function($q) use ($categories){
$query->whereIn('name', $categories);
}))->get();
答案 2 :(得分:0)
感谢您的回答,我用以下方法解决了这个问题:whereExists
和fromSub
的子选择array_agg
,运算符<@
(包含在其中):
$categories = ['category1', 'category2'];
Product::whereExists(function ($q) use ($categories){
$q->fromSub(function ($query) {
$query->selectRaw("array_agg(categories.name) as categoriesArr")
->from('categories')
->join('product_category', 'categories.id', '=', 'product_category.category_id')
->whereRaw('products.id = product_category.product_id');
}, 'foo')->whereRaw("categoriesArr <@ string_to_array(?, ',')::varchar[]", [
implode(",", $categories)
]);
})->get();