【Laravel】Eloquentでwith(eagerロード)を使用するかを正しく判断する

こんにちは。EloquentのEagerロードという機能が便利で以前より好んで使用しています。使っていてどのように使い分けをしたらよいかふと疑問に思い、公式のドキュメントを読んだところ、明確な答えを見つけたので記事にしてみました。

Eloquentは関連モデルを遅延ロードする

Eloquentのモデルにリレーションのメソッドを定義し、それを呼び出すことで関連するモデルを取得することができます。以下、公式のドキュメントにあるサンプルコードをもとに遅延ロードの説明をさせていただきます。

書籍モデルから著者のモデルへとリレーションを貼ります。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Book extends Model
{
    /**
     * その本を書いた著者を取得
     */
    public function author()
    {
        return $this->belongsTo(Author::class);
    }
}

書籍のモデルからautherメソッドを呼び出すことで、関連する著者が取得できるようになります。以下のように取得することができます。

use App\Models\Book;

$books = Book::all();

foreach ($books as $book) {
    echo $book->author->name;
}

書籍を全て取得しforeach内でautherメソッドを呼び出すことで著者名を取得しています。一見問題なさそうなのですが、このコードは注意が必要です。この場合、autherで取得する著者は遅延ローディングされるからです。

遅延ローティングとは、メソッドを呼び出した時にSQLを発行しデータを取得する動作のことです。仮にBookのデータが100件存在する時はforeach内で100回SQLが発行されることになります。Bookの件数にもよりますが、データベースサーバーにかなりの負荷をかけてしまう可能性があります。

Eagerロードでクエリ数を減らす

この問題を解決するのがEagerロードです。Eagerロードはその名の通り、遅延ロードに対し、積極的な(Eager)ロードを行います。書籍を取得する段階で関連する著者も全て取得する動作をします。先程のコードをEagerロードで書き換えたコードが以下です。

$books = Book::with(['author'])->get();

withメソッドを用いることでBookを取得するのと同じタイミングでautherを取得します。発行されるSQLは2件に抑えられます。Bookを取得するクエリと、関連するautherを取得するクエリです。(autherはBookのidをwhere inで取得するクエリが発行されます。)

いかがでしょうか。普段何気なく使用しているEagerロードですが、遅延ロードでクエリが大量に発行される問題を解決するために存在しているのです。

多くの場合、Eagerロードを使用することは問題になりませんが、使わなくてもよいケースもあります。それは関連するモデル(上の場合auther)を全て取得する必要がないときです。部分的に関連するモデルを参照する場合にEagerロードを使用するとかえってパフォーマンスの悪化を招くことになります。

まとめ

いかがでしょうか。普段何気なく使っているEagerロードですが、作られた背景を理解することで適切な使い分けができるようになります。取得するデータの件数で使用するかどうか都度判断するのがベストですね。

Laravel

Posted by kobainmac