Tối Ưu Query Laravel Với Eager Loading
Khi làm việc với Eloquent ORM của Laravel, một trong những "sát thủ" hiệu năng phổ biến nhất chính là vấn đề N+1 query. Nó xuất hiện ngay khi anh em load một collection model rồi đụng vào relationship bên trong vòng lặp.
Vấn đề N+1
$posts = Post::all(); // 1 query
foreach ($posts as $post) {
echo $post->author->name; // +1 query MỖI post
}
Load 50 bài viết là bắn ra 51 query liền: 1 cho posts, cộng thêm 1 cho mỗi author. Nhìn code thì không thấy gì, nhưng chạy dưới tải nặng là chết ngắc.
Cách fix: eager loading với with()
$posts = Post::with('author')->get(); // tổng cộng 2 query
foreach ($posts as $post) {
echo $post->author->name; // hết query dư
}
Laravel chạy 1 query lấy posts, rồi 1 query duy nhất kiểu WHERE author_id IN (...) để lấy hết author luôn - 2 query bất kể có bao nhiêu post đi nữa.
Nested & nhiều relationship cùng lúc
// Nhiều relation
$posts = Post::with(['author', 'comments'])->get();
// Relation lồng nhau (cú pháp dấu chấm)
$posts = Post::with('comments.user')->get();
// Ràng buộc relation bằng closure
$posts = Post::with(['comments' => fn ($q) => $q->latest()->limit(5)])->get();
Chỉ load cột nào cần thôi
$posts = Post::with('author:id,name')->get();
Chọn id,name giúp payload nhẹ hều - nhớ luôn kèm foreign key (id), không thôi relation nó không match được đâu.
Eager load sau khi đã có collection
Lỡ có collection rồi thì load relationship sau bằng load():
$posts = Post::all();
$posts->load('author');
Bắt N+1 tự động
Bật strict mode trong service provider để lazy loading tự quăng lỗi lúc dev:
use Illuminate\Database\Eloquent\Model;
Model::preventLazyLoading(! app()->isProduction());
Rút gọn lại
with()load trước (2 query);load()load thêm cho collection đã có sẵn.- Relationship nào đụng trong vòng lặp thì eager-load liền, đừng để lười.
- Dùng column selection với closure để chỉ lấy đúng thứ cần.
- Bật
preventLazyLoadinglúc dev để bắt N+1 trước khi lên production.
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í.
Code khởi tạo
$posts = Post::all();
foreach ($posts as $post) {
echo $post->author->name;
}
Lời giải của bạn
$posts = Post::with('author')->get();
foreach ($posts as $post) {
echo $post->author->name;
}

