Có thể bạn chưa biết "Has One Relation" trong laravel

Khi thao táo với relationships trong laravel, hầu hết chúng ta cũng cần đếm những element của bảng quan hệ phải không nào, ở mọi nơi trong trang web của chúng ta. Có rất nhiều cách để làm được điều này. Vấn đề đặt ra ở đây là cách nào tối ưu trong việc xử lý tình huống này nhé.

Chúng ta hãy cùng mổ xẻ vấn đề nhé 😄

Trước tiên chúng ta phải thiết lập relationship, ở đây mình lấy ngữ cảnh quen thuộc với chúng ta đó là:

// Post Model
public function comments()
{
    return $this->hasMany('Comment')
}

Và để đếm một bài post có bao nhiêu comment, cách thông thường chúng ta hay sử dụng nhất là:

$post = Post::first();

$post->comments->count();

$post->comments()->count();

Có vẻ nhìn khá là ổn

  1. $post->comments->count(): load theo collection và return kết quả count nhưng mong đợi. Collection thì performance rất là tệ, các Fan Laravel ai cũng hiểu về điều này.
  2. $post->comments()->count(): tốt hơn nhiều, execute query để xử lý, hãy cẩn thận, nó sẽ gọi query ở mọi nơi khi bạn sử dụng với foreach đấy Vậy thì dùng cách nào đây 😄. Đây mình sẽ giới thiệu cách này:
$posts = Post::leftJoin('comments', 'comments.post_id', '=', 'posts.id')
  ->select('posts.*', 'count(*) as commentsCount')
  ->groupBy('posts.id')
  ->get();

Thật tuyệt vời phải không nào! Viết nhưng vậy thì cũng không hay chút nào. Chúng ta còn xử lý "Eloquent eager load" blabla. Có cách nào xử lý clear hơn chút không? Giờ thì chúng ta chuyển cái đống loằng ngoằnh vào Postmodel thôi.

Let's go! Có 2 bước để chúng ta thực hiện điều này:

  1. Chúng ta xử dụng hasOne thay vì hasMany .
  2. Xử dụng accessor để dễ dàng trong việc lấy kết quả nhé.
// Post model
public function commentsCount()
{
  return $this->hasOne('Comment')
    ->selectRaw('post_id, count(*) as aggregate')
    ->groupBy('post_id');
}
 
public function getCommentsCountAttribute()
{
  // if relation is not loaded already, let's do it first
  if ( ! array_key_exists('commentsCount', $this->relations)) 
    $this->load('commentsCount');
 
  $related = $this->getRelation('commentsCount');
 
  // then return the count directly
  return ($related) ? (int) $related->aggregate : 0;
}

Giờ công việc của chúng ta sử dụng chúng một cách vô cùng đơn giản với Post model.

// lazy loading
$post = Post::find(1);
$post->commentsCount; 
 
// eager loading
$posts = Post::with('commentsCount')->get();
foreach ($post as $post) {
    echo $post->commentsCount;
}

Thật đơn giản phải không nào. Laravel thật tuyệt vời, laravel 5.3 đã nhận ra được điều này và đã hỗ trợ chúng ta trong việc xử lý vấn đề này, một cách nhanh chóng:

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

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

Với bài viết này chúng ta cũng hiểu cách mà Laravel sử dụng hasOne trong việc sử lý khéo léo đếm kết quả của bảng quan hệ với nó. Đây là link tham tham khảo : https://softonsofa.com/tweaking-eloquent-relations-how-to-get-hasmany-relation-count-efficiently/ Với Many to Many thì làm sao mà vận dụng được tình huống này đây 😄, hẹn gặp mọi người ở bài viết sau nhé.

Chúng mọi người cuối tuần vui vẻ 😃