如何用Laravel Eloquent按根品牌标题对多对多关联产品排序?
我明白你的问题了——你想按产品关联的根品牌标题给产品排序,但用with()里的orderBy()只是给每个产品关联的品牌集合内部排序,根本不会影响产品列表本身的顺序,对吧?
刚好你用了laravel-nestedset包,这个包会自动给品牌表维护root_id字段(每个品牌的root_id都会指向它所属的根节点ID,根品牌自己的root_id就是自身ID),这正好可以帮我们快速实现需求。
最优解决方案(利用nestedset内置字段)
直接通过数据库join关联到根品牌,然后按根品牌标题排序,这样是在数据库层面完成排序,性能更好:
$products = Product::query() // 关联多对多中间表 ->join('product_brand', 'products.id', '=', 'product_brand.product_id') // 关联品牌表,拿到每个关联品牌的root_id ->join('brands', 'product_brand.brand_id', '=', 'brands.id') // 通过root_id关联到根品牌表,获取根品牌的标题 ->join('brands as root_brands', 'brands.root_id', '=', 'root_brands.id') // 选择产品的所有字段,避免重复数据 ->select('products.*') // 分组去重:因为一个产品可能关联根+子品牌,会产生重复的产品记录 ->groupBy('products.id') // 按根品牌标题降序排序 ->orderBy('root_brands.title', 'desc') // 如果你需要加载产品关联的品牌集合,加上with ->with('brands') ->get();
替代方案(子查询方式)
如果你不想用join,也可以用子查询直接在排序逻辑里获取根品牌标题:
$products = Product::query() ->with('brands') ->orderByRaw('( SELECT title FROM brands WHERE id = (SELECT root_id FROM brands WHERE id = product_brand.brand_id) LIMIT 1 ) DESC') ->join('product_brand', 'products.id', '=', 'product_brand.product_id') ->groupBy('products.id') ->get();
为什么你之前的方法无效?
你之前写的:
$products_builder->with(['brands' => function($query) { $query->orderBy('title', 'desc'); }]);
这段代码的作用仅仅是当加载每个产品的关联品牌时,给该产品的brands集合内部按标题排序,完全不会改变产品列表的整体顺序,所以才达不到按根品牌排序产品的需求。
内容的提问来源于stack exchange,提问作者riaan Laubscher
相关产品推荐
相关产品推荐

