Laravel Eloquent:按关联价格对Product模型排序的问题
多价格关联产品的排序实现问题
数据表结构
pricing_types表
- id
- name(定价类型名称,如「个人用户」「企业用户」)
- percent(价格系数,如100表示加价100%,30表示折扣70%)
product_prices表
- id
- value(基准价格)
- pricing_type_id(关联pricing_types表)
- product_id(关联products表)
products表
- id
- name(产品名称)
- image(产品图片)
需求说明
每个产品对应多个不同群体的定价(通过product_prices关联不同pricing_types),需要按价格对产品进行排序,但尝试以下两种写法均未生效:
写法一(无效)
$q = Product::join('product_prices', function (JoinClause $join) { $join->on('product_prices.product_id', '=', 'products.id'); })->with(['category', 'prices', 'prices.pricingTypes'])->orderBy('product_prices.value', 'ASC')->simplePaginate(20)->withQueryString();
写法二(无效)
Product::with(['prices' => function ($q) { return $q->orderBy('value', 'ASC'); }], 'prices.pricingTypes')->simplePaginate(20)->withQueryString();
问题分析
- 写法一问题:直接join会产生重复的产品记录(一个产品对应多条price记录),分页时会把重复项计入总数,导致分页数据不准确;且排序是基于所有price的value,但产品重复展示,不符合需求。
- 写法二问题:
with中的orderBy仅对关联的prices集合内部排序,不会改变Product模型本身的排序顺序,产品仍按默认规则(如id)排序,而非价格。
解决方案
根据实际需求选择以下方案:
方案1:按产品的最低价格排序
通过子查询为每个产品附加「最低价格」字段,再按该字段排序:
Product::with(['category', 'prices', 'prices.pricingTypes']) ->select('products.*') ->selectSub(function ($query) { $query->selectRaw('MIN(value)') ->from('product_prices') ->whereColumn('product_prices.product_id', 'products.id'); }, 'min_price') ->orderBy('min_price', 'ASC') ->simplePaginate(20) ->withQueryString();
也可通过join+分组实现:
Product::join('product_prices', 'products.id', '=', 'product_prices.product_id') ->with(['category', 'prices', 'prices.pricingTypes']) ->select('products.*') ->selectRaw('MIN(product_prices.value) as min_price') ->groupBy('products.id') ->orderBy('min_price', 'ASC') ->simplePaginate(20) ->withQueryString();
方案2:按特定定价类型的价格排序
如果需要针对某类用户(如企业用户,假设pricing_type_id为2)的价格排序,可关联指定的price记录:
Product::leftJoin('product_prices', function ($join) { $join->on('products.id', '=', 'product_prices.product_id') ->where('product_prices.pricing_type_id', 2); // 替换为目标定价类型ID }) ->with(['category', 'prices', 'prices.pricingTypes']) ->select('products.*') ->orderBy('product_prices.value', 'ASC') ->simplePaginate(20) ->withQueryString();
注:若部分产品未设置该定价类型,
leftJoin会保留这些产品,排序时未关联的price值为null,会排在最前或最后,可根据需求调整排序规则(如orderByRaw('product_prices.value IS NULL, product_prices.value ASC'))。
内容的提问来源于stack exchange,提问作者bartezr
相关产品推荐
相关产品推荐

