Laravel中如何通过Eloquent基于状态列计算各仓库的总收入
如何用Eloquent计算每个仓库的总收入(区分发货与退回)
嘿,我完全懂你的困扰——普通的withSum()确实没法直接处理这种需要根据status区分加减的统计需求,不过有几种优雅的Eloquent方案可以解决这个问题,下面给你详细说说:
方法一:使用Join + 原生SQL聚合(推荐批量查询)
这种方法通过一次数据库查询就能拿到所有仓库的总收入,性能最优,适合批量查询场景:
$warehouses = Warehouse::query() ->select('warehouses.*') // 用CASE语句分别计算发货金额和退回金额,再做差额计算 ->selectRaw(' COALESCE(SUM(CASE WHEN sales.status = 1 THEN sales.product_price * sales.quantity ELSE 0 END), 0) - COALESCE(SUM(CASE WHEN sales.status = 2 THEN sales.product_price * sales.quantity ELSE 0 END), 0) AS total_revenue ') ->leftJoin('sales', 'warehouses.id', '=', 'sales.warehouse_id') ->groupBy('warehouses.id') ->get();
说明:
COALESCE用来处理没有销售记录的仓库,确保总收入显示为0而非nullleftJoin保证所有仓库都会被返回,哪怕没有对应的销售数据- 直接在数据库层面完成计算,避免PHP层面的额外处理
方法二:模型访问器(适合单个仓库查询)
如果只是查询单个仓库的总收入,可以给Warehouse模型添加一个访问器,延迟加载计算结果:
// 在Warehouse模型中添加 public function getTotalRevenueAttribute() { return $this->sales() ->selectRaw(' COALESCE(SUM(CASE WHEN status = 1 THEN product_price * quantity ELSE 0 END), 0) - COALESCE(SUM(CASE WHEN status = 2 THEN product_price * quantity ELSE 0 END), 0) AS revenue ') ->value('revenue') ?? 0; }
使用的时候直接访问属性即可:
$warehouse = Warehouse::find(1); echo $warehouse->total_revenue; // 输出仓库1的总收入
注意:
- 这种方法如果批量查询多个仓库,会触发N+1查询问题,所以批量场景不推荐
方法三:多次withSum() + PHP层面计算(直观易读)
如果你更倾向于用Eloquent的原生语法,不想写太多原生SQL,可以分两次统计发货和退回的总金额,再在PHP里做减法:
$warehouses = Warehouse::withSum( ['sales as shipped_total' => function ($query) { $query->where('status', 1)->selectRaw('product_price * quantity'); }] )->withSum( ['sales as returned_total' => function ($query) { $query->where('status', 2)->selectRaw('product_price * quantity'); }] )->get(); // 遍历计算每个仓库的总收入 foreach ($warehouses as $warehouse) { $totalRevenue = ($warehouse->shipped_total ?? 0) - ($warehouse->returned_total ?? 0); // 处理你的业务逻辑 }
说明:
- 这种写法更贴近Eloquent的惯用方式,可读性强
- 会执行两次子查询,性能和方法一差不多,看个人编码习惯选择
内容的提问来源于stack exchange,提问作者Othmane
相关产品推荐
相关产品推荐

