
本文介绍如何在 laravel 中通过关联查询,仅统计已“发货”(delivered)状态订单中的商品销量,并获取销量前三的商品列表。
在构建电商后台仪表盘时,常需展示“最畅销商品”,但若直接对 order_details 表聚合统计(如 SUM(quantity)),会忽略订单实际履约状态——例如,待支付、已取消或未发货的订单不应计入真实销售表现。因此,必须将 order_details 与关联的 orders 表进行条件联查,仅纳入 order_status = 'delivered' 的订单数据。
✅ 正确实现方式(基于 Eloquent 关系)
假设你的 OrderDetails 模型已正确定义了与 Order 模型的关联(例如 belongsTo(Order::class, 'order_id')),且 Order 模型中 status 字段存储订单状态(值为 'delivered' 等字符串),则可使用 whereHas() 方法高效完成筛选:
$top_sell_items = OrderDetails::with(['product', 'order']) // 预加载 product 和关联 order(推荐使用更语义化的 'order' 而非 'orders')
->whereHas('order', function ($query) {
$query->where('status', 'delivered');
})
->select('product_id', DB::raw('SUM(quantity) as total_quantity'))
->groupBy('product_id')
->orderBy('total_quantity', 'desc')
->take(3)
->get();? 关键点说明: whereHas('order', ...) 确保仅选取所属订单状态为 'delivered' 的明细记录; with(['product', 'order']) 支持后续访问 $item->product->name 或 $item->order->created_at 等字段,提升可扩展性; 列别名建议使用 total_quantity 替代 count,避免与 Eloquent 的 count() 方法语义混淆。
⚠️ 注意事项
-
关系命名一致性:确保 OrderDetails 模型中定义的关联方法名为 order()(单数,返回 belongsTo),而非 orders()(复数易引发误解)。若当前为 orders(),请先修正模型:
// 在 OrderDetails.php 中 public function order() { return $this->belongsTo(Order::class, 'order_id'); } -
数据库索引优化:为提升查询性能,建议在 orders.status 和 order_details.order_id 字段上建立联合索引:
CREATE INDEX idx_orders_status_order_id ON orders(status, id);
- 空结果处理:若无已发货订单,$top_sell_items 将为空集合,前端应做容错渲染(如显示“暂无已发货商品”)。
✅ 最终效果
执行后,$top_sell_items 将是一个包含 3 条 OrderDetails 实例的集合,每条均携带:
- product_id(商品 ID)
- total_quantity(该商品在所有已发货订单中的总销量)
- product(预加载的完整 Product 模型)
你可轻松在 Blade 模板中遍历展示:
@foreach($top_sell_items as $item)
{{ $item->product->name }} — {{ $item->total_quantity }} 件
@endforeach此方案兼顾可读性、性能与 Laravel 最佳实践,是处理跨表状态过滤聚合统计的标准解法。










