Tiếp tục với chuỗi bài về relationship trong Laravel Eloquent ORM. Phần này mình sẽ giới thiệu với mọi người về eager loading trong Eloquent ORM relationship.
1. Eager loading là gì?
Eager loading là một feature của Eloquent ORM, nó sẽ query các relations luôn ngay khi query của parent model được thực thi xong. Điều này sẽ giúp hạn chế vấn đề N+1 query của relationship trong Eloquent.
Mặc định, eloquent ORM relationship sẽ chỉ thực hiện query relation khi chúng ta truy vấn đến nó như một thuộc tính (nó sẽ truy vấn nếu như property này được gọi lần đầu tiên, các lần gọi tiếp theo nó sẽ lấy dữ liệu đã truy vấn trước đó). Điều này sẽ làm cho code của bạn sẽ bị dính lỗi "N + 1" query nếu bạn sử dụng chúng trong vòng lặp. Để cho dễ hiểu bạn có thể tham khảo ví dụ dưới đây.
VD: Mình có một model Book
và khai báo relation với Author
model (1 sách thuộc một tác giả).
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Book extends Model
{
/**
* Get the author that wrote the book.
*/
public function author()
{
return $this->belongsTo(Author::class);
}
}
Giờ nếu bạn lấy ra tất cả book và thông tin tác giả của sách đó theo cách thông thường.
use App\Models\Book;
$books = Book::all();
foreach ($books as $book) {
echo $book->author->name;
}
Lúc này đoạn code trên sẽ hoạt động như sau: Đầu tiên nó sẽ lấy hết $book
trong database ra, tiếp đó thực thi vòng lặp foreach để duyệt qua từng $book lúc này từng book sẽ thực hiện lấy ra author của book đó, ở hành động lấy author này đối với mỗi $book
được duyệt sẽ thực thi truy vấn đến database để lấy ra author của book id tương ứng. Đây chính là vấn đề "N+1" query.
Giả sử bạn có 10 cuốn sách thì đoạn code trên sẽ thực thi 11 câu query. Trong đó một câu query lấy ra tất cả 10 cuốn sách và 10 câu query để lấy ra từng author tương ứng với từng cuốn sách.
Đối với trường hợp trên, nếu như bạn sử dụng eager loading, thì nó chỉ thực thi duy nhất 2 câu query. Một câu lấy ra 10 cuốn sách sau đó và một câu query để lấy ra 10 author tương ứng với 10 cuốn sách đó (sử dụng WHERE IN rồi dùng code để mapping vào với author)
2. Sử dụng eager loading.
Để sử dụng eager loading trong Eloquent các bạn sử dụng phương thức with
.
VD: Đối với trường hợp ví dụ ở trên bạn có thể sử dụng eager loading để tối ưu query.
$books = Book::with('author')->get();
foreach ($books as $book) {
echo $book->author->name;
}
Lúc này SQL query sẽ có dạng như sau:
select * from books
select * from authors where id in (1, 2, 3, 4, 5, ...)
Bạn cũng có thể sử dụng phương thức with
để eager load nhiều relation cho một lần gọi, bằng cách truyền vào một array chứa các relation name.
VD:
$books = Book::with(['author', 'publisher'])->get();
Bạn cũng có thể eager load lồng nhiều cấp động bằng cách sử dụng ký tự ".
".
VD: Book model có relation Author
và Author
lại có relation đến Contract
, bạn có thể eager loading cả author và contract tương ứng như sau.
$books = Book::with('author.contacts')->get();
Mặc định Laravel sẽ lấy tất cả các column data trong relation (SELECT *
), nhưng nếu cần bạn có thể chỉ định những column nào cần lấy ra.
VD: Chỉ lấy ra column "id" và "name" của author.
$books = Book::with('author:id,name')->get();
Nếu như bạn muốn thiết lập eager loading luôn luôn được load cho model nào thì bạn có thể setting các relation vào thuộc tính $with
trong model.
VD: Luôn luôn sử dụng eager loading với relation 'author
' trong model Book
.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Book extends Model
{
/**
* The relationships that should always be loaded.
*
* @var array
*/
protected $with = ['author'];
/**
* Get the author that wrote the book.
*/
public function author()
{
return $this->belongsTo(Author::class);
}
}
Nếu bạn muốn loại bỏ eager loading cho một query nào đó trong modell đã thiết lập eager loading mặc định, bạn có thể sử dụng phương thức without
.
VD: Loại bỏ eager loading trong query đối với setting Book trong VD trên.
$books = Book::without('author')->get();
Nếu như bạn muốn sử dụng thêm điều kiện để query trong relation với eager loading mode, bạn có thể sử dụng giá trị của mảng trong phương thức with
là closure.
VD:
use App\Models\User;
$users = User::with(['posts' => function ($query) {
$query->where('title', 'like', '%code%');
}])->get();
4. Lời kết.
Phần này tương đối ngắn, nhưng nó rất quan trọng nếu bạn không hiểu về nó thì bạn sẽ tránh khỏi các vấn đề liên quan đến query, performace trong việc query đến Database trong LaraveL.
Đăng ký nhận tin.
Chúng tôi chỉ gửi tối đa 2 lần trên 1 tháng. Tuyên bố không spam mail!
Hoa
3 năm trước