Налаштування Laravel relationships — підрахунок коментарів (вільний переклад)

Представляю вам вільний переклад статті "Tweaking Eloquent relations – how to get hasMany relation count efficiently?" з сайту softonsofa.com.

Працюючи з відносинами моделей, ви найімовірніше хотіли б підрахувати кількість отриманих елементів (наприклад, коментарі чи лайки). Очевидно існують способи для цього, але не завжди ефективні, особливо коли ви завантажуєте колекцію моделей і їх відносини.

Що ж, дозвольте мені розповісти вам, що ми можемо з цим зробити.

hasMany relation

public function comments()
{
return $this->hasMany('Comment');
}

Найбільш прямолінійним способом може бути використання одного з двох методів нижче (можливо, обгорнутий в метод типу getCommentsCount в Post моделі):

[1] > $post = Post::first();
// object(Post)(
// 'incrementing' => true,
// 'timestamps' => true,
// 'exists' => true
// )
[2] > $post->comments->count();// 4
[3] > $post->comments()->count();// 4

Однак це все ще не найкраще рішення.

  • $post->comments->count(); завантажує колекцію і повертає кількість її елементів, не підходить, якщо вам потрібно отримати тільки кількість;
  • $post->comments()->count(); краще, не завантажує колекцію, але відправляє запит кожен раз, коли ми запускаємо метод.
Коли вам потрібно отримати колекцію постів і порахувати коментарі до них, то ви звичайно можете використовувати join, groupBy і все таке, але кого це хвилює? Чому ми повинні писати що зразок цього?

$posts = Post::leftJoin('comments', 'comments.post_id', '=', 'posts.id')
->select('posts.*', 'count(*) as commentsCount')
->groupBy('posts.id')
->get();

Якщо ми воліємо елегантний синтаксис в стилі Eloquent.

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

Саме це нам і потрібно, давайте нарешті зробимо це. Щоб змусити Eloquent предзагрузить кількість наших коментарів, ми повинні створити в моделі метод, возвращяющий Relation об'єкт.

public function comments()
{
return $this->hasMany('Comment');
}

public function commentsCount()
{
return $this->comments()
->selectRaw('post_id, count(*) as aggregate')
->groupBy('post_id');
}

Це звичайно працює, але я не був би самим собою, якби залишив все як є.

$post = Post::with('commentsCount')->first();

$post->commentsCount; 
$post->commentsCount->first(); 
$post->commentsCount->first()->aggregate; 

Давайте трохи покращимо на код.

  • Будемо використовувати hasOne замість hasMany, для того що б уникнути повернення колекції з одним елементом
  • Будемо використовувати метод доступу, для того що б просто порахувати коментарі

public function commentsCount()
{
return $this->hasOne('Comment')
->selectRaw('post_id, count(*) as aggregate')
->groupBy('post_id');
}

public function getCommentsCountAttribute()
{

if ( ! array_key_exists('commentsCount', $this->relations)) 
$this->load('commentsCount');

$related = $this->getRelation('commentsCount');


return ($related) ? (int) $related->aggregate : 0;
}

Тепер набагато простіше і красивіше працювати з цим.


$post = Post::first();
$post->commentsCount; // 4

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

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.