【问题标题】:Group by and Sum of One-To-Many relation tables in EloquentEloquent中一对多关系表的分组和总和
【发布时间】: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 列?

标签: mysql laravel eloquent eloquent-relationship


【解决方案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();

如果您使用PHPStanPsalm 之类的静态分析器,您也可以使用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();

【讨论】:

    猜你喜欢
    • 2020-09-08
    • 2021-10-09
    • 2019-10-06
    • 2023-04-07
    • 2017-04-20
    • 2022-11-12
    • 1970-01-01
    • 2013-02-09
    • 1970-01-01
    相关资源
    最近更新 更多