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/SummarizingHasMany关系
  • Counting/SummarizingHasManyThrough关系
  • Counting/SummarizingHasManyDeep关系

定义

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();

如果您使用像 PHPStanPsalm 这样的静态分析器,您也可以使用 scopes 方法来防止错误。

School::query()
    ->scopes(['withTotalSeats', 'withRegisteredStudents'])
    ->get();