Eager Loading trong Laravel


Mình sẽ cùng các bạn tìm hiểu Eager Loading trong Laravel hoạt động như thế nào và tại sao chúng ta nên sử dụng nó.

N+1 câu truy vấn

Nếu các bạn làm việc với laravel thì chắc chắn ít nhiều cũng đã sử dụng Eloquent relationships để thao tác với dữ liệu và sẽ có trường hợp bạn gặp phải vấn đề N+1 câu truy vấn.
Vậy N+1 câu truy vấn là gì?
Chúng ta đi tới một ví dụ để có thể hiểu rõ N+1 là gì.

  • Bạn có một danh sách các bài viết trong băng posts, mới Model Eloquent đặt tên là Post.

  • Mỗi Post thuộc về một User. Post có quan hệ belongsTo với User, và User có quan hệ hasMany vs Post.

    class Post extends Model
    {
      /**
       * Get the user that wrote the post.
       */
      public function user()
      {
          return $this->belongsTo('App\User'); // User::class - có nhiều cách khai báo
      }
    }
    
  • Bạn có một trang Post list, cần hiển thị ra một danh sách các Post, dĩ nhiên với tên tác giả của post đó. Đây là một tình huống rất hay thường gặp trong các project, với nhiều biến thể khác nhau. Và dưới đây là một cách giải quyết thường gặp.

    // In Controller or Somewhere else
    $posts = Post::all();
    // View
    foreach ($posts as $post) {
      {{ $post->title }}
      {{ $post->user->name }}
    }
    
  • Vòng lặp này sẽ thực hiện 1 truy vấn để truy xuất tất cả các bài Post trong data, sau đó một truy vấn khác cho mỗi cuốn sách để truy xuất tác giả. Vì vậy, nếu chúng ta có 320 bài post, vòng lặp này sẽ chạy 321 câu truy vấn. Các trên logic thực sự không sai nhưng bạn cần phải xem nó hoạt động thế nào:

-- Select the posts i.e. Post::all();

SELECT * FROM posts;
   

-- Foreach of the posts, another query to select the user

-- i.e. $post->user part of the loop

SELECT * FROM users WHERE user_id = 1

SELECT * FROM users WHERE user_id = 2

SELECT * FROM users WHERE user_id = 3

SELECT * FROM users WHERE user_id = 4

SELECT * FROM users WHERE user_id = 5

.....

Thật là kinh khủng nếu dữ liệu của chúng ta có hàng triệu cuốn sách. Tốc độ truy vấn sẽ rất lâu và khiến cho người dùng khó chịu khi phải chờ đợi, nhưng thật may khi ta có Eager Loading để giải quyết vấn đề này.
Chúng ta có thể dử dụng Eager Loading để số lượng xuống chỉ còn 2 truy vấn. Khi truy vấn, bạn có thể chỉ định những mối quan hệ nào sẽ dược eager loaded như sau:

$posts = Post::with('user')->get();

// view
foreach ($posts as $post) {
    {{ $post->title }}
    {{ $post->user->name }}
}

Đối với thao tác này chỉ có 2 truy vấn được thực hiện

-- Select the posts i.e. Post::all();
SELECT * FROM posts;


-- Just One time get all comments with for that posts
SELECT * FROM users WHERE user_id IN (1, 2, 3, 4, 5, ...);

Về cơ bản các bạn sẽ hiểu cách làm việc với eager loading và cách sử dụng nó và hãy áp dụng nó vào dự án thực tế của các bạn.

Eager Loading nhiều quan hệ

Đôi khi bạn muốn lấy nhiều quan hệ của một Model trong một truy vấn.
Để làm như vậy bạn chỉ cần truyền các đối số bổ sung cho phương thức with:

$posts = Post::with('user', 'comments')->get();

Eager Loading lồng nhau

Giả sử như bạn muốn hiển thị bài viết, hiển thị thông tin comments và thông tin người đã viết comment đó

$posts = Post::with('user', 'comments.user')->get();

Constraining Eager Loading

Đôi khi bạn muốn Eager Loading một quan hệ, nhưng cũng chỉ định các ràng buộc truy vấn bổ sung cho truy vấn Eager Loading.

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

Chỉ load những bài nào mà title có chứa từ “first”. Tất nhiên, bạn có thể gọi các phương thức xây dựng truy vấn khác để tùy chỉnh thêm hoạt động Eager Loading.

Eager Loading With Count

$posts = Post::withCount('comments')->get();
// comments_count

Eager Loading With Select Specific Columns

$posts = Post::with(['comments:id,body'])->get();

Lưu ý: Phải có field id

Lazy Eager Loading

Đôi khi bạn có thể cần Eager Loading một mối quan hệ sau khi đối tượng cha đã lấy được hoặc thỏa điều kiện mà chúng ta đặt ra. Điều này giúp chúng ta có quyền quyết định có chạy câu query thứ 2 để load các mối quan hệ hay không.

$posts = Post::all();

if ($someCondition) {
    $posts->load('user', 'comments');
}

Và còn nhiều hơn nữa do sự sáng tạo của các bạn, miễn sao nó không lỗi là được nhá. Mình hy vọng các bạn có thể hiểu eager loading trong laravel và áp dụng nó vào trong ứng dụng của bạn.

Kết luận

Lưu ý: Quan hệ đa hình sẽ không sử dụng được Eager Loading nha

Hãy luôn cố gắng dùng Eager Loading những lúc có thể nhé. Laravel cung cấp cho chúng ta một hệ thống Eager Loading rất hoàn hảo và mạnh mẽ.
Thông tin chi tiết về cách sử dụng Eager Loading các bạn có thể xem tại Official Document của Laravel

Rất mong được sự ủng hộ của mọi người để mình có động lực ra những bài viết tiếp theo.
{\__/}
( ~.~ )
/ > ♥️ I LOVE YOU 3000

JUST DO IT!


Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source Nguyễn Quang Đạt !
Comments
  TOC