孫リレーション先のカラムを合計する方法を解説してきます。
今回は詳しく解説していきませんが、この処理をすることで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でご確認を!