🦁

Laravel 12.8.0 で追加された自動リレーションロードで N+1 問題を解決

に公開

はじめに — N+1 と Laravel 12.8 の新機能

Eloquent を使った開発では、N+1 問題を避けるために with()load() を駆使するのが通例でした。しかしリレーションが増えるほど指定漏れや冗長なコードが生まれがちです。
Laravel 12.8.0 で登場した Automatic Relation Loading は、「リレーションにアクセスされた瞬間に自動で Eager Loading する」ことで N+1 をスマートに解決します。
この記事ではこの機能を紹介します。

1. N+1 問題 — もう一度おさらい

Eloquent で親モデル N 件を取得したあとに子リレーションへアクセスすると、合計 1 + N 回のクエリが発行されます。これがN+1 問題です。
パフォーマンス劣化を防ぐ一般的な解決策は Eager Loadingwith() / load())ですが、指定の漏れや深いネストでコードが冗長になる欠点がありました。

2. Automatic Relation Loading とは

Laravel 12.8.0 では、モデル/コレクションが シリアライズ(array や JSON 化)されるタイミングでアクセスされたリレーションを自動で loadMissing() する 機能が追加されました。
開発者は 「リレーションにアクセスする」 だけでよく、with() の書き忘れを気にする必要がありません。

3. 有効化方法

スコープ 手順 解除
アプリ全体 Model::automaticallyEagerLoadRelationships(); 引数に false を渡す
特定の取得結果だけ $collection->withRelationshipAutoloading(); 不要(そのコレクションにのみ作用)

アプリ全体

// 例: App\Providers\AppServiceProvider
public function boot(): void
{
    Model::automaticallyEagerLoadRelationships();
}

特定の取得結果だけ

public function postsWithComments(User $user): AnonymousResourceCollection
{
    $posts = $user->posts()->get();

    // 取得したコレクションに対して自動ロードを有効化
    $posts->withRelationshipAutoloading();

    return PostResource::collection($posts);
}

4. リソースクラスでの呼び出し

class PostResource extends JsonResource
{
    public function toArray($request): array
    {
        return [
            'id'       => $this->id,
            'title'    => $this->title,
            'comments' => CommentResource::collection($this->comments),
        ];
    }
}

本来であればこの場合、事前にwith()load()を実行してないとPostsレコードの件数分、Commentsテーブルに対してクエリが発行されますが、事前に3. の方法で有効化しておけばCommentsテーブルには1回のクエリ発行で済みます。

5. まとめ

  • Automatic Relation Loading は “使ったリレーションだけロードする” スマートな N+1 対策 になりそうです
  • グローバル or コレクション単位で手軽に ON/OFFの設定が可能です
  • ただし、導入時はLaravel Telescope や Laravel Debugbar でクエリの状態を監視し、意図せぬ挙動になってないか確認することをお勧めします

この新機能を上手に取り入れて、クリーンで高速な Eloquent コードを書いてみてください 🚀

参考:

Discussion