Laravel多对多关系查询:实现班级与科目关联数据分组展示
解决Laravel多对多关系下按班级分组展示科目代码的问题
问题分析
当前通过join查询返回的是班级-科目的关联明细数据,同一个班级会对应多条记录,导致前端循环时重复渲染班级行,无法实现“一行一个班级+对应所有科目代码”的展示效果。
解决方案
方案1:利用Eloquent关联预加载(推荐)
首先确保你的Classe模型(注意Class是PHP关键字,建议重命名为Classe)已定义与Subject的多对多关联:
// app/Models/Classe.php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Classe extends Model { protected $table = 'classes'; // 定义与科目多对多关联 public function subjects() { // 参数依次:关联模型、中间表名、当前模型外键、关联模型外键 return $this->belongsToMany(Subject::class, 'classe_subjects', 'class', 'subject'); } }
修改ClassController的index方法,预加载科目关联,避免N+1查询问题:
public function index() { // 预加载每个班级对应的所有科目 $classes = \App\Models\Classe::with('subjects')->get(); return view('classes.index', compact('classes')); }
更新前端classes/index.blade.php模板,循环每个班级并遍历其科目:
<table class="table table-striped table-responsive"> <thead class="table-bordered"> <tr> <th>班级名称</th> <th>科目</th> <th>学生人数</th> <th>操作</th> </tr> </thead> <tbody> @foreach($classes as $class) <tr> <td>{{ $class->name }}</td> <td> @foreach($class->subjects as $subject) <span class="border border-secondary border-radius bg-secondary-light p-1">{{ $subject->code }}</span> @endforeach </td> <td> <span class="border border-secondary border-radius bg-secondary-light p-1">{{ $class->student_count ?? 50 }}</span> </td> <td> <a href="{{ route('classes.show', $class->id) }}"><i class="bi bi-eye text-secondary"></i></a> <a href="{{ route('classes.edit', $class->id) }}"><i class="bi bi-pencil-square text-primary"></i></a> <a href="#" onclick="event.preventDefault(); document.getElementById('delete-form-{{ $class->id }}').submit();"><i class="bi bi-trash text-danger"></i></a> <form id="delete-form-{{ $class->id }}" action="{{ route('classes.destroy', $class->id) }}" method="POST" style="display: none;"> @csrf @method('DELETE') </form> </td> </tr> @endforeach </tbody> </table>
方案2:使用SQL分组聚合(GROUP_CONCAT)
如果希望直接在查询中拼接好科目代码,可修改Controller的查询逻辑:
public function index() { $classes = \App\Models\Classe::select('classes.*') ->join('classe_subjects', 'classes.id', '=', 'classe_subjects.class') ->join('subjects', 'classe_subjects.subject', '=', 'subjects.id') // 用GROUP_CONCAT拼接科目代码,逗号分隔 ->selectRaw('classes.*, GROUP_CONCAT(subjects.code SEPARATOR ", ") as subject_codes') // 按班级ID分组 ->groupBy('classes.id') ->get(); return view('classes.index', compact('classes')); }
对应前端模板简化为:
<td>{{ $class->subject_codes }}</td>
效果说明
两种方案都能实现:每行展示一个班级名称,对应列显示该班级的所有科目代码(方案1为标签形式,方案2为逗号分隔的文本形式),与你期望的展示效果一致。
内容的提问来源于stack exchange,提问作者Med Sidatt
相关产品推荐
相关产品推荐

