Laravel にはデータベースに対応するモデルクラスである Eloquent が用意されており、その中ではテーブル同士の関係を表現するリレーションという仕組みも用意されています。これを次の様に使うことで JOIN 句を用いずとも N+1 問題を避けつつ、いい感じに SQL が実行され、データを呼び出せます。
// 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 ですがしばしばリレーション先を細かい制御をしつつ呼び出したい時があります。例えば論理削除を無視して呼び出す時などです。そういった時は次の様に書けます。
$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 など構造化されたデータの方が望ましい局面では特に便利な機能です。