Eloquent collections extend Laravel's base Collection class, so the usual collection methods apply — but there are a couple of Eloquent-specific traps worth knowing.
Basic checks
$users = User::where('active', true)->get();
$users->count(); // int
$users->isEmpty(); // bool
$users->isNotEmpty(); // bool
count() on a query vs. count() on a collection
These look identical but do very different work:
User::where('active', true)->count(); // SQL: SELECT COUNT(*) ...
User::where('active', true)->get()->count(); // fetches every row, then counts in PHP
If you only need the number, the first form is almost always better — it never pulls the actual rows into memory. Only use ->get()->count() when you're already loading the collection for another reason and counting it is free.
Checking if a relationship is empty without an N+1 problem
A common but costly pattern:
foreach ($posts as $post) {
if ($post->comments->isEmpty()) { // lazy-loads comments per post
// ...
}
}
Each $post->comments access triggers its own query unless the relationship was eager-loaded. Fix it with eager loading, or better, with a query-level existence check that never loads the related rows at all:
$posts = Post::withCount('comments')->get();
foreach ($posts as $post) {
if ($post->comments_count === 0) {
// ...
}
}
withCount() runs a single additional aggregate query for the whole collection, rather than one query per row — the fix for the N+1 pattern above, not just a stylistic preference.
whereHas() vs a PHP-side filter
If the goal is filtering posts that have zero comments (not just checking, but excluding), do it in the query, not after fetching:
$posts = Post::whereDoesntHave('comments')->get();
This is a single SQL query with a NOT EXISTS subquery — far cheaper than fetching every post and filtering in PHP once the table has any real size.