6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

EagerLoadingでLaravelのN+1問題を解決するテクニック

6
Posted at

はじめに

LaravelでEloquent ORMを使っていると、知らず知らずのうちに N+1クエリ問題 に陥ることがあります。
本記事では、この問題を根本から解決する Eager Loading(イーガーローディング) というテクニックをわかりやすく解説します。

1. Eager Loadingとは?

Eager LoadingはEloquent ORMの機能の一つで、親モデルのクエリが実行された直後に、
関連するリレーションも一緒にクエリする
仕組みです。
これにより、Eloquentのリレーションシップで発生しがちな「N+1クエリ問題」を解消することができます。

N+1クエリ問題とは?

デフォルトでは、Eloquent ORMのリレーションシップはプロパティとしてアクセスされたときに
初めてクエリが実行されます。このため、ループ内でリレーションにアクセスすると
「N+1クエリ問題」が発生します。

Book モデルが Author モデルとリレーション(1冊の本は1人の著者に属する)を持つ例:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

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

通常の方法で全ての本とその著者情報を取得する場合:

use App\Models\Book;

$books = Book::all();

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

10冊の本がある場合、このコードは 合計11回 のクエリを実行します。

  • 全10冊を取得する:1クエリ
  • 各本の著者を取得する:10クエリ

これが 「N+1クエリ問題」 です。

Eager Loadingを使えば、同じ処理が わずか2クエリ で済みます!

2. Eager Loadingの使い方

基本的な使い方

with メソッドを使ってEager Loadingを利用できます。

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

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

発行されるSQLクエリ:

select * from books
select * from authors where id in (1, 2, 3, 4, 5, ...)

複数のリレーションをまとめてEager Load

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

ネストされたリレーションをEager Load

BookAuthorContact のようにネストされている場合:

$books = Book::with('author.contacts')->get();

取得するカラムを指定する

デフォルトでは全カラム(SELECT *)が取得されますが、必要なカラムだけを指定できます。

// authorのidとnameだけを取得
$books = Book::with('author:id,name')->get();

常にEager Loadする設定

モデルの $with プロパティに設定することで、常にEager Loadingを適用できます。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Book extends Model
{
    /**
     * 常にロードするリレーションシップ
     *
     * @var array
     */
    protected $with = ['author'];

    public function author()
    {
        return $this->belongsTo(Author::class);
    }
}

特定のクエリでEager Loadingを除外する

without メソッドで、デフォルトのEager Loadingを除外できます。

$books = Book::without('author')->get();

条件付きEager Loading

クロージャを使って、リレーションに条件を付けることもできます。

use App\Models\User;

$users = User::with(['posts' => function ($query) {
    $query->where('title', 'like', '%code%');
}])->get();

まとめ

方法 クエリ数
通常のリレーション(N+1) N+1回
Eager Loading(with 2回

Eager Loadingを理解して活用することで、LaravelアプリケーションのDBパフォーマンスを
大幅に改善できます。ぜひ積極的に使ってみてください!

6
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?