You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Laravel中实现嵌套GroupBy(分组内再分组)

如何实现用户-项目嵌套分组的工时数据查询?

表结构说明

reports表是记录用户每月工作投入的关联表,结构如下:

iduser_idproject_idtotal_effortcreated_at
15232402023-01-23

需求与现有问题

需要获取用户下各项目贡献明细的嵌套结构数据,用于生成Excel表格。目前仅能通过以下查询实现用户级汇总,无法实现「用户→项目」的嵌套分组:

$data = Report::select('user_id')
            ->selectRaw("SUM(total_effort) as total, DATE_FORMAT(report_for_date, '%b,%Y') new_date")
            ->groupBy('user_id')->with('userDetail:id,first_name,last_name')->get();

一、Laravel Eloquent 实现嵌套结构

可以通过「多维度分组查询+集合重组」或「关联预加载」两种方式实现嵌套结构:

方法1:先查询用户-项目层级数据,再重组结构

先获取用户与项目的关联汇总数据,再利用Collection的groupBy方法重组为嵌套结构:

// 查询用户-项目-月份的层级汇总数据,关联用户、项目信息
$rawData = Report::select('user_id', 'project_id')
    ->selectRaw("SUM(total_effort) as total_effort, DATE_FORMAT(report_for_date, '%b,%Y') as report_month")
    ->groupBy('user_id', 'project_id', 'report_month') // 按用户、项目、月份多维度分组
    ->with('userDetail:id,first_name,last_name')
    ->with('project:id,name') // 若有Project模型,可关联项目名称等信息
    ->get();

// 重组为用户嵌套项目的结构
$nestedData = $rawData->groupBy('user_id')->map(function ($userReports) {
    $userInfo = $userReports->first()->userDetail;
    return [
        'user' => $userInfo,
        'projects' => $userReports->groupBy('project_id')->map(function ($projectReports) {
            $projectInfo = $projectReports->first()->project ?? null;
            return [
                'project' => $projectInfo,
                'monthly_records' => $projectReports->pluck(['total_effort', 'report_month'])
            ];
        })
    ];
});

最终$nestedData即为外层用户、内层项目的嵌套结构,可直接循环生成Excel。

方法2:利用模型关联预加载处理

若User模型已定义reports关联,可通过预加载时的闭包实现分组:

// 在User模型中定义关联
public function reports()
{
    return $this->hasMany(Report::class);
}

// 查询用户并预加载按项目分组的工时数据
$users = User::with(['reports' => function ($query) {
    $query->select('user_id', 'project_id')
        ->selectRaw("SUM(total_effort) as total, DATE_FORMAT(report_for_date, '%b,%Y') as new_date")
        ->groupBy('project_id', 'new_date');
}])->get();

返回的每个User实例中,reports属性已按项目分组,可直接遍历使用。


二、原生MySQL查询实现多维度分组

原生MySQL无法直接返回嵌套结构,但可以通过多字段分组获取扁平的层级数据,再在应用层重组:

SELECT 
    r.user_id,
    u.first_name,
    u.last_name,
    r.project_id,
    p.name AS project_name,
    SUM(r.total_effort) AS total_effort,
    DATE_FORMAT(r.report_for_date, '%b,%Y') AS report_month
FROM reports r
LEFT JOIN users u ON r.user_id = u.id
LEFT JOIN projects p ON r.project_id = p.id
GROUP BY r.user_id, r.project_id, report_month
ORDER BY r.user_id, r.project_id;

该查询返回「用户-项目-月份」的扁平汇总数据,你可以在代码中循环遍历,将相同user_id的记录归为一组,再在每组内按project_id二次分组,最终形成嵌套结构。

内容的提问来源于stack exchange,提问作者user7747472

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 02:20:25