N+1問題とは「Lazy Loading」
Eloquentモデルのリレーションの呼び出しは「Lazy Loading」
そのプロパティが呼ばれてからSQLが実行される。
<?php $animais = Animal::all(); // select * from animals; (+1回) foreach ($animals as $animal) { $animal->owner->name; // select * from owners where id = $animal->owner_id (N回) }
SELECT文(ownersテーブルへの問い合わせクエリ)が取得件数分だけ実行されてしまうので、時に重大なパフォーマンス低下を引き起こすんや。 これがN+1問題や。
N+1問題を解決する「Eager Loading」とは
リレーション元のモデルを取得直後に、関連する子モデルのデータを全て取得しておくこと。
Eager Loadeingにはwithメソッドを利用する。
引数にはプロパティ名を指定する。
<?php $animais = Animal::with('owner')->get(); // select * from animals; (1回) foreach ($animals as $animal) { $animal->owner->name; // select * from owners where id in (1,2,3,.....) (1回)
with句を使うことでクエリの実行が2回で済むんやな。
「Eager Loading」複数リレーションの場合
with句の引数にリレーション先のプロパティ名を複数渡す。
<?php $animais = Animal::with('owner','country')->get(); foreach ($animals as $animal) { $animal->owner->name; $animal->country->name; }
「Eager Loading」リレーション先のリレーション先を取得する場合
with句の引数でドット記法を使う。
<?php $animais = Animal::with('owner','owner.tel')->get(); foreach ($animals as $animal) { $animal->owner->name; $animal->owner->tel->tel_number; }
「Eager Loading」対象をクロージャで絞り込む
<?php $owners = Owner::with ([ 'animals' => function($query) { $query->where('***')->orderBy('***'); } ])->get(); foreach ($owners as $owner) { foreach ($owner->animals as $animal) { echo $animal->name; } }