Через withCount() і схожі методи - вони додають до основного запиту підзапит, і число приходить окремим атрибутом.
$posts = Post::withCount('comments')->get();
foreach ($posts as $post) {
echo $post->comments_count; // без завантаження коментарів
}
Чому не $post->comments->count(): це завантажить усі коментарі кожного поста в пам'ять, а без with() ще й дасть N+1 запитів - лише заради одного числа.
Родина методів:
Post::withCount(['comments', 'comments as approved_count' => fn ($q) => $q->where('approved', true)])
->withExists('likes') // likes_exists: true / false
->withSum('orders', 'total') // orders_sum_total
->withMax('comments', 'created_at')
->get();
Фільтр, а не підрахунок, - це has() і whereHas():
Post::has('comments', '>=', 10)->get();
Post::whereHas('comments', fn ($q) => $q->where('approved', true))->get();
Для вже завантаженої колекції є loadCount(). А якщо лічильник показують на кожній сторінці й рахувати дорого, його зберігають у колонці (comments_count) і оновлюють подіями - денормалізація в обмін на швидкість читання.