🦁
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 Loading(with()
/ 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