
本文详解在 Laravel 中通过 Eloquent 预加载(eager loading)结合聚合查询,为多个用户统一获取按日分组的等级变化统计(如 min_level、max_level、sum_level_change),并解决因 groupBy 与 selectRaw 使用不当导致预加载返回空数组的问题。
本文详解在 laravel 中通过 eloquent 预加载(eager loading)结合聚合查询,为多个用户统一获取按日分组的等级变化统计(如 min_level、max_level、sum_level_change),并解决因 `groupby` 与 `selectraw` 使用不当导致预加载返回空数组的问题。
在 Laravel 开发中,当我们需要为单个用户高效获取近 7 天的聚合统计(例如每日最低/最高等级、等级变更总和),使用 selectRaw + groupByRaw 配合模型关联是常见做法。但当扩展到多个用户(如一个房间下的所有用户)时,直接将单用户逻辑套入 with() 预加载往往失败——典型表现为 statistics 关系返回空数组 []。
根本原因在于:Eloquent 的 with() 预加载默认执行的是「一对一/一对多」的关联查询,而聚合查询(含 GROUP BY)会改变结果集结构,导致框架无法正确映射到子模型实例上。尤其当 statistics 是一对多关系(如 User hasMany Statistics)时,groupBy 后每条记录已非原始统计表行,Eloquent 无法自动关联到父 user_id。
✅ 正确解法是使用 带约束的预加载(Constrained Eager Loading),并确保聚合查询逻辑完整、字段可映射:
return $room->users()
->with('statistics' => function ($query) {
$query->selectRaw('user_id, DAY(created_at) as day, DATE(created_at) as date, MIN(level) as min_level, MAX(level) as max_level, SUM(level_change) as sum_level_change')
->whereDate('created_at', '>=', now()->subDays(7))
->groupBy('user_id', 'day', 'date') // ⚠️ 关键:必须包含 user_id 以维持关联上下文
->orderBy('sum_level_change', 'desc'); // 按 sum_level_change 降序(题设要求)
})
->limit(25)
->get();
? 关键要点说明:
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
-
必须显式
SELECT user_id:这是预加载能正确绑定统计结果到对应用户的前提。否则 Eloquent 无法识别哪条聚合记录属于哪个用户。 -
GROUP BY需包含分组维度与外键:此处为user_id,day,date(推荐用DATE(created_at)而非DAY(),避免跨月问题)。 -
避免
latest()在预加载中使用:latest()依赖created_at排序,但聚合后该字段已失真;应改用orderBy('date', 'desc')或业务所需字段(如本例按sum_level_change排序)。 -
日期处理建议:用
DATE(created_at)替代DAY(date),确保跨月数据不被错误归并(例如2022-09-01和2022-10-01的DAY均为 1,但属不同月份)。
? 进阶提示:若需最终结果按 sum_level_change 全局排序(而非每个用户内部排序),则需转为 JOIN 查询或使用集合操作:
$usersWithStats = $room->users()
->with('statistics' => fn ($q) => $q->selectRaw('user_id, DATE(created_at) as date, MIN(level) as min_level, MAX(level) as max_level, SUM(level_change) as sum_level_change')
->whereDate('created_at', '>=', now()->subDays(7))
->groupBy('user_id', 'date'))
->limit(25)
->get();
// 将嵌套统计扁平化,并全局排序
$flattened = $usersWithStats->flatMap(function ($user) {
return $user->statistics->map(fn ($stat) => $stat->merge(['user_id' => $user->id, 'user_name' => $user->name]));
})->sortByDesc('sum_level_change');
总结:为多用户获取一致聚合统计,核心是在预加载中保留外键、明确分组维度、避免破坏关联映射的语法陷阱。遵循此模式,即可安全复用单用户逻辑,同时获得高性能、可维护的批量统计能力。










