Laravel 8通过三个关联模型统计各学年去重学生人数问题
实现方案
1 先确认模型关联配置
Student和Group为多对多关系(一个学生可加入多个群组,一个群组包含多个学生),如果还没配置正确关联,先调整模型代码:
- Student模型中群组关联调整为:
public function groups() { return $this->belongsToMany(Group::class); }
- Group模型中学生关联保持多对多即可,Major和Student的一对多关联无需调整。
2 编写统计逻辑(SQL层实现,性能最优)
推荐直接用查询构建器在SQL层完成统计,避免查询全量数据后处理的性能损耗,适合生产环境使用。以下是Major控制器index方法的示例代码:
public function index() { $majors = Major::query() ->select('majors.*') // 批量统计1-7学年的独立学生数 ->when(true, function ($query) { for ($year = 1; $year <= 7; $year++) { $query->selectSub( function ($subQuery) use ($year) { $subQuery->from('students') ->join('group_student', 'students.id', '=', 'group_student.student_id') ->join('groups', 'group_student.group_id', '=', 'groups.id') ->whereColumn('students.major_id', 'majors.id') ->where('groups.course_year', $year) ->count('DISTINCT students.id'); }, "year_{$year}_count" ); } }) ->get(); return view('major.index', compact('majors')); }
逻辑说明
- 用
DISTINCT students.id去重,确保同一个学生同一学年加入多个群组仅统计1次 - 各学年统计独立,学生加入不同学年的群组会分别计入对应学年的人数,完全符合统计规则
3 视图层展示
在专业列表Blade模板中直接读取统计字段即可:
<table> <thead> <tr> <th>专业名称</th> @for($year = 1; $year <=7; $year++) <th>{{ $year }}学年人数</th> @endfor </tr> </thead> <tbody> @foreach($majors as $major) <tr> <td>{{ $major->name }}</td> @for($year = 1; $year <=7; $year++) <td>{{ $major->{"year_{$year}_count"} ?? 0 }}</td> @endfor </tr> @endforeach </tbody> </table>
可选方案:集合处理(适合小数据量场景)
如果专业、学生总数据量小于1万,也可以用更直观的集合处理方式实现:
$majors = Major::query() ->with('students.groups') ->get() ->map(function ($major) { $yearCounts = array_fill(1, 7, 0); foreach ($major->students as $student) { // 拿到学生加入群组的所有学年,去重后计数 $joinYears = $student->groups->pluck('course_year')->unique(); foreach ($joinYears as $year) { isset($yearCounts[$year]) && $yearCounts[$year]++; } } $major->year_counts = $yearCounts; return $major; });
内容的提问来源于stack exchange,提问作者Ivan Slavchev
相关产品推荐
相关产品推荐

