孫リレーション先のカラムを合計する方法を解説してきます。
今回は詳しく解説していきませんが、この処理をすることでN+1問題を解決できる機会も多くあるかと思います。
子リレーションの場合は、withSum(‘{{子リレーション}}’, ‘{{対象カラム}}’)を利用して合計していけると思います。
同様に、withSum(‘{{子リレーション.孫リレーション}}’, ‘{{孫の対象カラム}}’)でいけるかな?と思っていたらNGでした。
■解決方法
①:親→孫リレーションを作成
②:親→孫リレーションにて、withSumで集計
という2ステップで実施します。
親子孫のモーチーフとしては、
学校毎のテスト点数の合計を集計する方法を考えていきます。
School → Student → Score とつながっていきます。
①:親→孫リレーションを作成
まずは、親から孫を繋ぐためのリレーションを作成します。
Modelを親、子、孫 2つ用意します。
子:Student Model
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Student extends Model { use HasFactory; protected $fillable = [ 'name', 'school_id', ]; public function score() { return $this->hasMany(Score::class, 'student_id', 'id'); } }
孫:Score Model
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Score extends Model { use HasFactory; protected $fillable = [ 'score', 'student_id', ]; public function student() { return $this->belongsTo(Student::class, 'id', 'student_id'); } }
親:School Model
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class School extends Model { use HasFactory; protected $fillable = [ 'name', ]; // このリレーションが今回の本題です。 親→孫へのリレーションを作成 public function studentscore() { return $this->hasManyThrough( Score::class, // 孫 Model Student::class, // 子 Model 'school_id', // 子テーブルの親ID 'student_id', // 孫モデルの子ID 'id', // 親テーブルのローカルキー 'id' // 子テーブルのローカルキー ) } }
ポイントは、School Model の hasManyThroughを利用したリレーションメソッドです。
(親→子、子→孫へのリレーションもお忘れなく作成しておいてください。)
上記にて、親→孫リレーションを作成が出来ました。
②:親→孫リレーションにて、withSumで集計
②-1:孫からデータを合計
学校毎の学生の点数を合計する処理を書いていきます。
任意のControllerにて、
$school_score = School:: withSum('student_score', 'score') ->get();
こちらで、学校毎に学生の点数合計が取得出来ます。
②-2:合計データはどこに?
合計データはどこに含まれるのでしょうか。
A. 親データに新しいカラムが追加されます。 カラム名は、
{{親孫のリレーション名}}_sum_{{合計したカラム名}}
$school_scoreの中身に、下記のカラムが追加さています。
studentscore_sum_score
ddでご確認を!