Laravel にはデータベースに対応するモデルクラスである Eloquent が用意されており、その中ではテーブル同士の関係を表現するリレーションという仕組みも用意されています。これを次の様に使うことで JOIN 句を用いずとも N+1 問題を避けつつ、いい感じに SQL が実行され、データを呼び出せます。
1 2 3 4 5 6 7 8 9 10 11 12 13 | // with メソッドで Laravel がリレーション元を使って // いい感じに SELECT * FROM リレーション先 WHERE IN () しつつ // 結果をリレーション元のプロパティに追加してくれます。 $users = User::with( 'podcasts' )->get(); // もし↑で with していない場合、 // ここでループの度に SELCT * FROM podcasts WHERE user_id = ? の様な SQL を発行してしまいます foreach ( $users ->flatMap->podcasts as $podcast ) { echo $podcast ->subscription->created_at; } // 最初のクエリ発行時に with を使わずとも load メソッドでリレーション先を呼べます。 $users ->load( 'podcasts' ) |
こんな感じで便利な with や load ですがしばしばリレーション先を細かい制御をしつつ呼び出したい時があります。例えば論理削除を無視して呼び出す時などです。そういった時は次の様に書けます。
1 2 3 4 5 6 7 8 9 10 | $users = User::with([ // ここではキーで指定したリレーションメソッド名が返すリレーションクエリクラスのインスタンスを受け取るクロージャを使えます。 'podcasts' => static function (\Illuminate\Database\Eloquent\Relations\HasMany $query ) { // Laravel 組み込みの論理削除はグローバルスコープに↓のクラス名で登録されています $query ->withoutGlobalScope(\Illuminate\Database\Eloquent\SoftDeletingScope:: class ) ->where( 'type' , PodcastType::HOGE); // 普通のSQLで使うような句も構築できます } ])->get(); // load の場合も同様に配列で指定できます。 |
このあたりを駆使していくと素の JOIN に近い感覚のクエリを構築することを少なくできます。「表で一覧画面を見たい」などの要望があった場合はどうにも JOIN 句込みの SQL が便利ですが、API など構造化されたデータの方が望ましい局面では特に便利な機能です。