Eloquent中一对多关系表的分组和总和
Posted
技术标签:
【中文标题】Eloquent中一对多关系表的分组和总和【英文标题】:Group by and Sum of One-To-Many relation tables in Eloquent 【发布时间】:2022-01-03 15:35:33 【问题描述】:我有一个要求。 我的数据库有如下表。 这些表具有 OneToMany (1-n) 父子关系。
Table School (id, school_name)
Table Class (id, school_id, class_name)
Table Section (id, class_id, section_name, no_of_seats)
Table Student (id, section_id, student_name, ....)
当 Some Student 注册后,数据会上传到 Student 表。 现在,我想要一个类似的统计数据
| school_name | total_seats | student_registered |
对于特定的学校
| class_name | total_seats | student_registered |
如何在 Laravel/Eloquent 中实现这一点
提前致谢
【问题讨论】:
你能分享一些模型代码和你正在使用的列 上面给出了我的 4 个模型/表以及列名。学校、班级、部门和学生。 在这些模型中是否定义了关系?? 是的,正确定义。例如,Class 表将 school_id 作为 School 表 id 列的外键。 Section 和 Student 表也定义了关系。 您是否已经在任何表中定义了total_seats
列?
【参考方案1】:
可能适用于:
计数/总结HasMany
关系
计数/总结HasManyThrough
关系
计数/总结HasManyDeep
关系
定义
class Section extends Model
public function students(): HasMany
return $this->hasMany(Student::class);
public function scopeWithRegisteredStudents(Builder $query): Builder
// Count HasMany relation
return $query->withCount('students as students_registered');
// The word "Class" is reserved, so we need to use "SchoolClass" instead
class SchoolClass extends Model
protected $table = 'classes';
public function sections(): HasMany
return $this->hasMany(Section::class, 'class_id');
public function students(): HasManyThrough
return $this->hasManyThrough(Student::class, Section::class, 'class_id');
public function scopeWithTotalSeats(Builder $query): Builder
// Summarize field from HasMany relation
return $query->withSum('sections as total_seats', 'no_of_seat');
public function scopeWithRegisteredStudents(Builder $query): Builder
// Count HasManyThrough relation
return $query->withCount('students as students_registered');
class School extends Model
public function classes(): HasMany
return $this->hasMany(SchoolClass::class);
public function sections(): HasMany
return $this->hasManyThrough(Section::class, SchoolClass::class, null, 'class_id');
public function students(): HasManyThrough
// https://github.com/staudenmeir/eloquent-has-many-deep
return $this->hasManyDeep(Student::class, [SchoolClass::class, Section::class], ['school_id', 'class_id', 'section_id'], ['id', 'id', 'id']);
public function scopeWithTotalSeats(Builder $query): Builder
// Summarize field from HasManyThrough relation
return $query->withSum('sections as total_seats', 'no_of_seat');
public function scopeWithRegisteredStudents(Builder $query): Builder
// Count HasManyDeep relation
return $query->withCount('students as students_registered');
示例
// Fetching simply
Section::query()
->withRegisteredStudents()
->get();
SchoolClass::query()
->withTotalSeats()
->withRegisteredStudents()
->get();
School::query()
->withTotalSeats()
->withRegisteredStudents()
->get();
// Fetching with nested relations
School::query()
->withTotalSeats()
->withRegisteredStudents()
->with(['classes' => function (HasMany $query)
return $query
->withTotalSeats()
->withRegisteredStudents();
])
->get();
如果您使用phpStan
或Psalm
之类的静态分析器,您也可以使用scopes
方法来防止错误。
School::query()
->scopes(['withTotalSeats', 'withRegisteredStudents'])
->get();
【讨论】:
谢谢@mpyw,非常感谢。它按预期工作。【参考方案2】:这不是您所要求的,因为它使用 Query Builder 而不是 Eloquent。我没有测试它,因为我目前没有什么要测试的,但这应该可以工作 -
use Illuminate\Support\Facades\DB;
$students_per_section = DB:table('students')
->select('section_id', DB::raw('COUNT(id) AS num_students'))
->groupBy('section_id')
$query = DB:table('schools')
->join('classes', 'schools'.'id', '=', 'classes.school_id')
->join('sections', 'classes.id', '=', 'sections.class_id')
->leftJoinSub($students_per_section, 'students_per_section', function($join)
$join->on('sections.id', '=', 'students_per_section.section_id')
);
if ($school_id)
$query
->select('classes.class_name', DB::raw('SUM(no_of_seats) AS total_seats'), DB::raw('SUM(students_per_section.num_students) AS student_registered'))
->where('schools.id', '=', $school_id)
->groupBy('classes.class_name')
else
$query
->select('schools.school_name', DB::raw('SUM(no_of_seats) AS total_seats'), DB::raw('SUM(students_per_section.num_students) AS student_registered'))
->groupBy('schools.school_name')
$stats = $query->get();
【讨论】:
以上是关于Eloquent中一对多关系表的分组和总和的主要内容,如果未能解决你的问题,请参考以下文章
无法删除一对多关系中的记录 laravel eloquent
在 ORM(Eloquent)中,具有 Genre 的 Book 是一对多关系吗?