Laravel多层嵌套关联场景下如何正确统计products表total字段总和
你原代码的核心问题是在with()的关联约束闭包中直接调用了sum()方法,该方法会立即执行查询返回数值结果,而with()要求约束闭包必须返回查询构造器实例,因此整套嵌套预加载逻辑直接失效,最终仅执行了主表crop_types的全量查询。
最优解决方案(Laravel 8+ 推荐)
直接使用withSum()方法支持嵌套关联的点语法,一行代码即可完成每个crop_type对应所有产品total字段的总和统计:
$data = CropType::withSum('categories.varieties.products', 'total')->get();
执行后每个CropType实例会自动生成categories_varieties_products_sum_total属性,存储你需要的总和值。如果你要自定义统计字段别名,可以用withAggregate方法:
$data = CropType::withAggregate('categories.varieties.products', 'sum(total) as total_product_amount')->get();
最终可通过$item->total_product_amount访问统计结果。
兼容低版本Laravel方案
如果你的Laravel版本低于8不支持上述方法,可以用关联join的方式实现统计,性能同样高效:
$data = CropType::selectRaw('crop_types.*, SUM(products.total) as total_product_amount') ->leftJoin('categories', 'categories.crop_type_id', '=', 'crop_types.id') ->leftJoin('varieties', 'varieties.category_id', '=', 'categories.id') ->leftJoin('products', 'products.variety_id', '=', 'varieties.id') ->groupBy('crop_types.id') ->get();
注意:如果你的MySQL开启了
ONLY_FULL_GROUP_BY模式,需要确保groupBy的字段和select中的非聚合字段完全对应,上述写法中crop_types.id为主键,因此select crop_types.*不会触发语法错误。
需要同时加载全量嵌套关联的方案
如果你不仅需要总和,还要加载所有层级的关联数据,可以按如下方式改写:
$data = CropType::with([ 'categories' => function($cq) { $cq->with(['varieties' => function($vq) { // 统计每个品种的产品总和 $vq->withSum('products', 'total'); }]); } ]) // 统计当前作物类型的总产品量 ->withSum('categories.varieties.products', 'total') ->get();
内容的提问来源于stack exchange,提问作者Andreas Hunter
相关产品推荐
相关产品推荐

