Laravel中如何对pivot中间表字段求和 实现销售订单发货数量校验
前置修正
你现有SalesOrder模型里的shipments关联定义有误,因为一个销售单支持多次发货,应该改为hasMany而不是hasOne:
// SalesOrder.php 修正后 public function shipments() { return $this->hasMany(Shipment::class); }
实现方案
完全可以通过Eloquent直接统计已发货数量,不需要嵌套foreach遍历,这里提供两种常用实现方式:
方式1:SQL直接统计(数据量大时更推荐,性能更高)
直接通过关联查询统计当前销售单下各商品的总发货量,再和订单商品的下单数量对比:
use Illuminate\Support\Facades\DB; // 假设$salesOrderId为指定销售单的ID $salesOrder = SalesOrder::with('products')->findOrFail($salesOrderId); // 统计当前销售单所有发货单的各商品发货总量 $shippedQtyMap = DB::table('product_shipment') ->join('shipments', 'product_shipment.shipment_id', '=', 'shipments.id') ->where('shipments.sales_order_id', $salesOrderId) ->groupBy('product_id') ->pluck(DB::raw('SUM(qty) as total_shipped'), 'product_id'); // 计算各商品可发货最大数量 $availableShipQty = $salesOrder->products->map(function ($product) use ($shippedQtyMap) { $orderedQty = $product->pivot->qty; $shippedQty = $shippedQtyMap->get($product->id, 0); return [ 'product_id' => $product->id, 'ordered_qty' => $orderedQty, 'shipped_qty' => $shippedQty, 'max_ship_qty' => max(0, $orderedQty - $shippedQty) ]; })->keyBy('product_id');
方式2:封装为模型访问器(方便全局调用)
可以把逻辑封装到SalesOrder模型的访问器中,后续用的时候直接调用即可:
// 在SalesOrder.php中新增访问器 public function getMaxShipQuantitiesAttribute() { $shippedQtyMap = DB::table('product_shipment') ->join('shipments', 'product_shipment.shipment_id', '=', 'shipments.id') ->where('shipments.sales_order_id', $this->id) ->groupBy('product_id') ->pluck(DB::raw('SUM(qty)'), 'product_id'); return $this->products->mapWithKeys(function ($product) use ($shippedQtyMap) { $available = max(0, $product->pivot->qty - $shippedQtyMap->get($product->id, 0)); return [$product->id => $available]; }); }
调用方式非常简单:
$salesOrder = SalesOrder::findOrFail($salesOrderId); // 直接获取商品ID为5的最大可发货数量 echo $salesOrder->max_ship_quantities[5];
额外建议
创建发货单时建议加表单验证,校验本次提交的各商品发货数量不超过对应的最大可发货值,避免超发。
内容的提问来源于stack exchange,提问作者Kevin Jonathan
相关产品推荐
相关产品推荐

