Quay lại

Eager Loading Và Lazy Loading Và Eager Lazy Loading Trong Laravel Chuyên mục PHP và Laravel    2024-05-22    31 Lượt xem    30 Lượt thích    comment-3 Created with Sketch Beta. 0 Bình luận

Eager Loading Và Lazy Loading Và Eager Lazy Loading Trong Laravel

Trong Laravel, Eloquent ORM cung cấp các phương pháp để tải dữ liệu quan hệ nhằm tối ưu hóa truy vấn cơ sở dữ liệu và cải thiện hiệu suất ứng dụng. Ba phương pháp chính để tải dữ liệu quan hệ là Lazy Loading, Eager Loading, và Lazy Eager Loading. Dưới đây là giải thích chi tiết về từng phương pháp cùng với các ví dụ minh họa.

Lazy Loading

Lazy Loading là phương pháp mặc định trong Eloquent. Với Lazy Loading, dữ liệu quan hệ chỉ được tải KHI bạn truy cập vào mối quan hệ lần đầu tiên. Điều này có thể dẫn đến vấn đề N + 1 query nếu bạn lặp qua nhiều bản ghi và truy cập vào mối quan hệ của chúng.

Ví dụ:

$users = User::all();

foreach ($users as $user) {
    echo $user->name . ' has ' . $user->posts->count() . ' posts.';
}​

Trong ví dụ trên, mỗi lần bạn truy cập $user->posts, một truy vấn mới sẽ được thực hiện để lấy các bài viết của user đó. Nếu có 10 users, và mỗi user có 5 posts, Laravel sẽ thực hiện 11 truy vấn (1 truy vấn để lấy tất cả users và 10 truy vấn để lấy posts cho từng user).

Dưới đây là cách Laravel sẽ thực hiện các truy vấn trong trường hợp này:

  1. Truy vấn đầu tiên để lấy tất cả users:
SELECT * FROM `users`;​

Giả sử truy vấn này trả về 10 users:

$users = User::all();​
  1. Truy vấn riêng biệt cho mỗi user để lấy các posts của user đó:

Trong vòng lặp, Laravel sẽ thực hiện một truy vấn riêng cho mỗi user để lấy các posts của user đó:

foreach ($users as $user) {
    echo $user->name . ' has ' . $user->posts->count() . ' posts.' . PHP_EOL;
}​

Trong trường hợp này, $user->posts->count() sẽ thực hiện một truy vấn để đếm số lượng posts của mỗi user:

SELECT COUNT(*) FROM `posts` WHERE `user_id` = 1;
SELECT COUNT(*) FROM `posts` WHERE `user_id` = 2;
SELECT COUNT(*) FROM `posts` WHERE `user_id` = 3;
...
SELECT COUNT(*) FROM `posts` WHERE `user_id` = 10;​

Như vậy, nếu có 10 users, Laravel sẽ thực hiện 1 truy vấn để lấy tất cả users và thêm 10 truy vấn riêng biệt để lấy số lượng posts của mỗi user, tổng cộng là 11 truy vấn.

Eager Loading

Eager Loading giúp tránh vấn đề N + 1 query bằng cách tải trước các quan hệ thông qua phương thức with(). Điều này thực hiện các truy vấn cần thiết chỉ một lần khi bạn tải các đối tượng chính.

Ví dụ:

$users = User::with('posts')->get();

foreach ($users as $user) {
    echo $user->name . ' has ' . $user->posts->count() . ' posts.';
}​

Trong ví dụ này, Laravel sẽ thực hiện hai truy vấn:

  1. Truy vấn đầu tiên để lấy tất cả users.
  2. Truy vấn thứ hai để lấy tất cả posts quan hệ đến các users đó.

Điều này tránh được việc thực hiện nhiều truy vấn nhỏ cho từng user và cải thiện hiệu suất.

Quy Trình Chi Tiết Cách with() Hoạt Động
  1. Truy Vấn Lấy Tất Cả Các Bản Ghi Chính:

Khi bạn gọi User::with('posts')->get(), truy vấn đầu tiên được thực hiện để lấy tất cả các bản ghi từ bảng users:

SELECT * FROM `users`;​

Giả sử bạn nhận được kết quả như sau:

$users = [
    (object) ['id' => 1, 'name' => 'Alice'],
    (object) ['id' => 2, 'name' => 'Bob'],
    (object) ['id' => 3, 'name' => 'Charlie'],
    ...
    (object) ['id' => 10, 'name' => 's2sontech'],
];​
  1. Truy Vấn Lấy Tất Cả Các Bản Ghi Liên Quan:

Laravel sau đó thực hiện một truy vấn thứ hai để lấy tất cả các bản ghi từ bảng posts có user_id nằm trong danh sách các id của các users đã được lấy ở truy vấn đầu tiên:

SELECT * FROM `posts` WHERE `user_id` IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

Giả sử bạn nhận được kết quả như sau:

$posts = [
    (object) ['id' => 1, 'user_id' => 1, 'title' => 'Post 1'],
    (object) ['id' => 2, 'user_id' => 1, 'title' => 'Post 2'],
    (object) ['id' => 3, 'user_id' => 2, 'title' => 'Post 3'],
    (object) ['id' => 4, 'user_id' => 3, 'title' => 'Post 4'],
    (object) ['id' => 5, 'user_id' => 3, 'title' => 'Post 5'],
];​
  1. Ánh Xạ Kết Quả:

Laravel sau đó ánh xạ các bản ghi posts vào các bản ghi users tương ứng trong bộ nhớ. Quá trình này tự động được thực hiện bởi Eloquent, sử dụng các mối quan hệ đã được định nghĩa trong các model.

Cách Laravel Thực Hiện Ánh Xạ

Laravel sử dụng các mối quan hệ đã được định nghĩa trong các model để ánh xạ các bản ghi. Ví dụ, nếu bạn đã định nghĩa quan hệ hasMany trong model User:

class User extends Model
{
    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}​

Laravel sẽ sử dụng quan hệ này để ánh xạ các bản ghi posts vào các bản ghi users:

  • Khởi tạo một mảng trống posts trong mỗi đối tượng user.
    • // Khởi tạo mảng trống 'posts' cho mỗi user
      foreach ($users as $user) {
          $user->posts = [];
      }
      
      // Thêm các post vào mảng 'posts' của user tương ứng
      foreach ($posts as $post) {
          foreach ($users as $user) {
              if ($user->id == $post->user_id) {
                  $user->posts[] = $post;
              }
          }
      }
      
  • Duyệt qua từng post và thêm nó vào mảng posts của user tương ứng dựa trên user_id.

Laravel thực hiện điều này trong bộ nhớ mà không cần sự can thiệp thủ công từ phía bạn.

Ví Dụ Minh Họa

Sau khi ánh xạ, dữ liệu sẽ trông như thế này trong bộ nhớ:

$users = [
    (object) ['id' => 1, 'name' => 'Alice', 'posts' => [
        (object) ['id' => 1, 'user_id' => 1, 'title' => 'Post 1'],
        (object) ['id' => 2, 'user_id' => 1, 'title' => 'Post 2'],
    ]],
    (object) ['id' => 2, 'name' => 'Bob', 'posts' => [
        (object) ['id' => 3, 'user_id' => 2, 'title' => 'Post 3'],
    ]],
    (object) ['id' => 3, 'name' => 'Charlie', 'posts' => [
        (object) ['id' => 4, 'user_id' => 3, 'title' => 'Post 4'],
        (object) ['id' => 5, 'user_id' => 3, 'title' => 'Post 5'],
    ]],
];​

Bây giờ, khi bạn truy cập thuộc tính posts của mỗi user, bạn sẽ thấy rằng các post đã được tải trước và không cần thực hiện thêm truy vấn nào:

// users/index.blade.php
@foreach ($users as $user)
    <p>{{ $user->name }} has {{ $user->posts->count() }} posts.</p>
@endforeach​

Điều này cho phép bạn tránh được vấn đề N + 1 query và tối ưu hóa hiệu suất của ứng dụng một cách đáng kể. Laravel Eloquent thực hiện quá trình ánh xạ này tự động, giúp bạn làm việc với dữ liệu một cách hiệu quả và dễ dàng hơn.

Lazy Eager Loading

Lazy Eager Loading là một phương pháp kết hợp giữa Lazy Loading và Eager Loading. Nó cho phép bạn tải trước các mối quan hệ sau khi đã thực hiện truy vấn ban đầu để lấy các đối tượng chính. Điều này có nghĩa là bạn có thể tải trước quan hệship sau khi đã thực hiện xử lý dữ liệu ban đầu.

Ví dụ:

// Lấy tất cả users
$users = User::all();

// Thực hiện một số xử lý dữ liệu trên tập hợp users
$activeUsers = $users->filter(function ($user) {
    return $user->is_active;
});

// Tải trước quan hệ posts cho các users đã được lọc
$activeUsers->load('posts');

foreach ($activeUsers as $user) {
    echo $user->name . ' has ' . $user->posts->count() . ' posts.';
}​

Trong ví dụ này:

  1. $users = User::all(); - Lấy tất cả users.
  2. $activeUsers = $users->filter(...) - Lọc các users dựa trên một điều kiện nhất định (ví dụ: chỉ các users đang hoạt động).
  3. $activeUsers->load('posts'); - Tải trước các posts quan hệ đến các users đã được lọc.

Tóm tắt

  • Lazy Loading: quan hệship được tải khi bạn truy cập vào nó lần đầu tiên, có thể dẫn đến vấn đề N + 1 query.
  • Eager Loading: Mối quan hệ được tải trước thông qua phương thức with(), tránh vấn đề N + 1 query và cải thiện hiệu suất.
  • Lazy Eager Loading: Tải trước mối quan hệ sau khi đã thực hiện xử lý dữ liệu ban đầu, kết hợp tính linh hoạt của Lazy Loading và hiệu suất của Eager Loading.

Sử dụng các phương pháp này một cách phù hợp sẽ giúp bạn tối ưu hóa truy vấn cơ sở dữ liệu và cải thiện hiệu suất ứng dụng Laravel của mình.

Bình luận (0)

Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough

Bài viết liên quan

Learning English Everyday