
本文介绍如何在 laravel 中通过关联查询,仅统计“已交付”(delivered)状态订单中的商品销量,并准确获取 top 3 畅销商品。
在构建电商或订单类后台仪表盘时,常需动态展示“最畅销商品”。但若直接对 order_details 表聚合销量(如 SUM(quantity)),会忽略订单实际履约状态——例如将已取消、待发货或退款中的订单也计入销量,导致数据失真。要精准统计“已交付”商品的销量,关键在于跨表条件过滤 + 聚合分组。
Laravel 提供了优雅的解决方案:利用 whereHas() 方法,在聚合前先对关联的 orders 表施加状态约束。前提是已在 OrderDetails 模型中正确定义与 Order 的关联关系(如 belongsTo)。示例代码如下:
$top_sell_items = OrderDetails::with(['product', 'order']) // 推荐使用 'order'(单数)而非 'orders',更符合一对一语义
->whereHas('order', function ($query) {
$query->where('order_status', 'delivered'); // 注意字段名应为 'order_status'(与问题描述一致)
})
->select('product_id', DB::raw('SUM(quantity) as total_quantity'))
->groupBy('product_id')
->orderBy('total_quantity', 'desc')
->take(3)
->get();⚠️ 注意事项:
- 关联方法名需与模型中定义一致(如 order() 而非 orders(),除非明确是一对多);
- orders 表中状态字段名为 order_status(非 status),请根据实际数据库结构调整;
- 若 order_details.order_id 未建立外键或关联缺失,需先在 OrderDetails 模型中补充:
public function order() { return $this->belongsTo(Order::class, 'order_id'); } - 如需在结果中显示商品名称、图片等信息,with(['product']) 已确保懒加载;若需进一步优化 N+1 问题,可结合 select() 显式指定所需字段。
最终返回的 $top_sell_items 是 OrderDetails 集合,每个元素包含 product_id 和 total_quantity,并通过 product 关系可访问完整商品信息。此方案兼顾性能(单次 SQL 查询)、可读性与 Laravel 最佳实践。










