Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Eloquentのリレーション正しく理解する 〜with()を使ったのにEagerロードされない!?

Avatar for sito sito
June 27, 2025

Eloquentのリレーション正しく理解する 〜with()を使ったのにEagerロードされない!?

PHP Conference Japan 2025のLT登壇資料です。

Avatar for sito

sito

June 27, 2025
Tweet

Other Decks in Programming

Transcript

  1. 16 // クエリ(※説明のため大幅に簡略化しています) $contracts = Contract::all(); foreach ($contracts as $contract)

    { $paymentInfo = $contract->paymentInfo()->first(); } // リレーション定義 class Contract extends Model { public function paymentInfo() { return $this->hasOne(PaymentInfo::class); } } 改修 Before
  2. // クエリ(※説明のため大幅に簡略化しています) $contracts = Contract::with("paymentInfo")->get(); foreach ($contracts as $contract) {

    $paymentInfo = $contract->paymentInfo()->first(); } 17 class Contract extends Model { public function paymentInfo() { return $this->hasOne(PaymentInfo::class); } } 改修 After // リレーション定義 class Contract extends Model { public function paymentInfo() { return $this->hasOne(PaymentInfo::class); } }
  3. 2種類のリレーション • $post->comments() … リレーションメソッド • $post->comments … 動的プロパティ ◯

    Eagerロードを適用するには「動的プロパティ」を   使用する必要がある 27
  4. 動的プロパティ $contract->paymentInfo 戻り値は Eloquent Collection または Model • get() や first()

    をチェーンせず取得可能 取得結果をキャッシュする → 2回目以降の呼び出しではクエリが発行されない 33
  5. 動的プロパティ(少し深堀り) 1. 動的プロパティにアクセス 2. __get() マジックメソッドが呼ばれる 3. キャッシュの有無を確認し、あればそれを返す 4. 無ければリレーションメソッドを呼び出す

    5. getResults() した結果をキャッシュし、これを返す 37 ※各ステップに関する説明はスライド末尾の補足資料を参照ください  (時間の都合上LTでは省略します)
  6. 動的プロパティ(少し深堀り) 1. 動的プロパティにアクセス 2. __get() マジックメソッドが呼ばれる 3. キャッシュの有無を確認し、あればそれを返す 4. 無ければリレーションメソッドを呼び出す

    5. getResults() した結果をキャッシュし、これを返す 38 ※各ステップに関する説明はスライド末尾の補足資料を参照ください  (時間の都合上LTでは省略します)
  7. $contracts = Contract::with("paymentInfo")->get(); foreach ($contracts as $contract) { $paymentInfo =

    $contract->paymentInfo()->first(); } 再改修 Eagerロードしたいので、動的プロパティに書き換える 39 Before
  8. 「1つのクエリで取得したデータをもとに、さらにN回のクエリが発行さ れ、結果として合計でN+1回のクエリが実行される状況」 補足:N+1問題とは(ざっくり) 49 class Post extends Model { public

    function comments() { return $this->hasMany(Comment::class); } } $posts = Post::all(); foreach ($posts as $post) { echo $post->comments; } SELECT * FROM posts;                1回 SELECT * FROM comments WHERE post_id = ?;    N回(postの件数分) N+1 postが100件あったら、(この4行で)クエリが101回実行される
  9. $posts = Post::with("comments") ->get(); foreach ($posts as $post) { echo

    $post->comments; } 「親のモデルに対するクエリと同時にリレーションデータも取得する」 補足:Eagerロードとは(ざっくり) SELECT * FROM posts;  SELECT * FROM comments WHERE post_id IN (?, ?, ... → post が何件あろうと、クエリ2回で取得できる 50 対して、リレーションデータへのアクセス時にクエリを発行するものを「Lazyロード」と いう。 N+1問題の原因になるが、メモリ消費を抑えられるなどのメリットもある。
  10. 補足:動的プロパティ(少し深堀り) 1. 動的プロパティにアクセス 2. __get() マジックメソッドが呼ばれる 3. キャッシュの有無を確認し、あればそれを返す 4. 無ければリレーションメソッドを呼び出す

    5. getResults() した結果をキャッシュし、これを返す ◦ Illuminate\Database\Eloquent\Concerns\HasAttributes ◦ Illuminate\Database\Eloquent\Concerns\HasRelationships 56