17370845950

如何在 Laravel 中按订单状态筛选并统计热销商品

本文介绍如何在 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 最佳实践,是处理跨表状态过滤聚合统计的标准解法。