Trung bình Laravel

Laravel Query Optimization with Eager Loading

Learn how to optimize database queries in Laravel by using eager loading to prevent N+1 query problems.

19 Th06, 2026 2 phút 17 Lượt xem 6 Khối code

Laravel Query Optimization with Eager Loading

When working with Laravel's Eloquent ORM, one of the most common performance killers is the N+1 query problem. It appears the moment you load a collection of models and then touch a relationship inside a loop.

The N+1 problem

$posts = Post::all();            // 1 query

foreach ($posts as $post) {
    echo $post->author->name;    // +1 query PER post
}

Load 50 posts and you fire 51 queries: one for the posts, plus one for every author. It is invisible in code review but deadly under load.

flowchart TD A[Post all - 1 query] --> B{Loop each post} B --> C[post.author - query 2] B --> D[post.author - query 3] B --> E[post.author - query 51] style C fill:#fee2e2,stroke:#ef4444 style D fill:#fee2e2,stroke:#ef4444 style E fill:#fee2e2,stroke:#ef4444

The fix: eager loading with with()

$posts = Post::with('author')->get();   // 2 queries total

foreach ($posts as $post) {
    echo $post->author->name;            // no extra queries
}

Laravel runs one query for posts and a single WHERE author_id IN (...) for all authors - 2 queries no matter how many posts.

Nested & multiple relationships

// Multiple relations
$posts = Post::with(['author', 'comments'])->get();

// Nested relations (dot notation)
$posts = Post::with('comments.user')->get();

// Constrain a relation with a closure
$posts = Post::with(['comments' => fn ($q) => $q->latest()->limit(5)])->get();

Load only the columns you need

$posts = Post::with('author:id,name')->get();

Selecting id,name keeps the payload small - just always include the foreign key (id) or the relation will not match.

Eager load after the fact

Already have the collection? Load relationships afterwards with load():

$posts = Post::all();
$posts->load('author');

Catch N+1 automatically

Enable strict mode in a service provider so lazy loading throws during development:

use Illuminate\Database\Eloquent\Model;

Model::preventLazyLoading(! app()->isProduction());

Key takeaways

  • with() eager-loads upfront (2 queries); load() eager-loads an existing collection.
  • Always eager-load any relationship you touch inside a loop.
  • Use column selection and closures to load only what you need.
  • Turn on preventLazyLoading in dev to catch N+1 before it ships.
Thử thách tương tác

Thử thách

Luyện tập ngay điều vừa học. Viết lời giải, mở gợi ý nếu bí.

Đề bài

The loop below reads $post->author inside each iteration, firing one extra query per post — the classic N+1 problem. Rewrite the query so the author relationship is eager-loaded, and the whole list runs in just 2 queries.

Ngôn ngữPHP

Code khởi tạo

php
$posts = Post::all();

foreach ($posts as $post) {
    echo $post->author->name;
}

Lời giải của bạn

Lời giải · php
$posts = Post::with('author')->get();

foreach ($posts as $post) {
    echo $post->author->name;
}