Laravel多关联模型下按part_id分组求和quantity字段问题
问题与解决方案
问题核心
需要对Production表按part_id分组并求和quantity,同时加载关联的Part、Job、Material数据,但当前查询仅能加载Part,Job返回null、Material为空集合。原因是:
- 分组查询仅选取了
part_id和聚合字段,未包含Production主键id及job_id,导致预加载无法匹配关联数据; - 手动
join与Eloquent预加载逻辑冲突,分组后的数据已不是完整的Production模型实例。
解决方案
方法一:调整查询字段,适配预加载逻辑
如果需要保留Production模型实例并加载关联,需在查询中包含模型主键id、job_id,同时严格模式下分组字段需覆盖所有非聚合字段:
$data = Production::with('part', 'materials', 'job') ->where('job_id', $job->id) ->groupBy('id', 'part_id', 'job_id') // 严格模式必填所有非聚合字段 ->selectRaw('id, part_id, job_id, SUM(quantity) as total') ->get();
注:此方法会按每个Production实例分组,若需同一part_id下的总quantity(合并多个Production),请使用方法二。
方法二:分离聚合查询与关联加载
先获取分组聚合结果,再单独加载对应模型的关联数据:
- 获取聚合统计:
$aggregates = DB::table('productions') ->where('job_id', $job->id) ->groupBy('part_id') ->selectRaw('part_id, SUM(quantity) as total') ->get();
- 加载关联模型:
$partIds = $aggregates->pluck('part_id')->toArray(); $productions = Production::with('part', 'materials', 'job') ->where('job_id', $job->id) ->whereIn('part_id', $partIds) ->get();
- (可选)合并聚合数据到模型:
$combinedData = $productions->groupBy('part_id')->map(function ($group) use ($aggregates) { $total = $aggregates->firstWhere('part_id', $group->first()->part_id)->total; return $group->each(fn($item) => $item->total = $total); })->flatten();
方法三:通过关联模型反向查询(推荐)
利用Part模型的关联关系,更贴合Eloquent设计逻辑,同时直接获取聚合数据:
$parts = Part::with(['productions.job', 'productions.materials']) ->whereHas('productions', fn($query) => $query->where('job_id', $job->id)) ->withCount(['productions as total_quantity' => function ($query) use ($job) { $query->where('job_id', $job->id)->select(DB::raw('SUM(quantity)')); }]) ->get();
此方法返回的每个Part实例包含total_quantity字段,同时预加载了对应的Production、Job、Material关联数据。
关键注意事项
- 避免手动
join与模型预加载混用,会破坏Eloquent对模型主键的识别; - MySQL严格模式下,
groupBy必须包含所有非聚合的选中字段; - 多对多关联依赖模型主键,需确保查询结果包含Production的
id字段,或通过关联模型反向查询。
内容的提问来源于stack exchange,提问作者Nick Kist
相关产品推荐
相关产品推荐

