Eloquent 中一对多关系表的分组和求和
Group by and Sum of One-To-Many relation tables in Eloquent
我有一个要求。
我的数据库有 table 如下所示。
table 具有 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, ....)
当一些学生注册时,数据被上传到学生table。
现在,我想要一个像
这样的统计数据
| school_name | total_seats | student_registered |
和特定学校
| class_name | total_seats | student_registered |
如何在 Laravel/Eloquent
中实现
提前致谢
这不是您要求的,因为它使用查询生成器而不是 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();
可能适用于:
- Counting/Summarizing
HasMany
关系
- Counting/Summarizing
HasManyThrough
关系
- Counting/Summarizing
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();
我有一个要求。 我的数据库有 table 如下所示。 table 具有 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, ....)
当一些学生注册时,数据被上传到学生table。 现在,我想要一个像
这样的统计数据| school_name | total_seats | student_registered |
和特定学校
| class_name | total_seats | student_registered |
如何在 Laravel/Eloquent
中实现提前致谢
这不是您要求的,因为它使用查询生成器而不是 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();
可能适用于:
- Counting/Summarizing
HasMany
关系 - Counting/Summarizing
HasManyThrough
关系 - Counting/Summarizing
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();