Eloquent Relationship Existence: Checks
Eloquent Relationship Existence: Checks
Eloquent Relationship Existence: Checks
Tip: exists() on Relation
if ($post->comments()->exists()) {
// Has comments
}
Single EXISTS query.
Gotcha: count() vs exists()
$post->comments()->count() > 0; // Loads count
$post->comments()->exists(); // Faster - just checks existence
Tip: doesntExist()
if ($post->comments()->doesntExist()) {
// No comments
}
Gotcha: Loaded Relation Check
if ($post->comments->isNotEmpty()) {
// Checks loaded collection
}
Only works if relation is already loaded.
Tip: whereHas with Count
Post::whereHas('comments', fn($q) => $q, '>=', 5)->get();
Posts with 5+ comments.
Gotcha: has() Shorthand
Post::has('comments')->get();
Posts with at least one comment.
Tip: Use cursor() for Memory-Neutral Iteration
When exporting 100K rows, get() loads everything into memory. cursor() uses yield and keeps memory flat regardless of row count. Perfect for artisan commands.
Tip: Pivot Data Is Read-Only by Default
Many-to-many pivot columns are read-only unless you define custom pivot models. Use ->withPivot('expires_at') to make them accessible, and newPivot() to make them writable.
Gotcha: withCount() Adds a Subquery
withCount('comments') runs a correlated subquery on every row. On large tables, this can be slower than a separate query. Profile before relying on it.
Senior Insight
Checking relationship existence with has() is convenient, but the generated SQL varies significantly between database engines. On PostgreSQL, has() generates an EXISTS clause. On SQLite, it generates a subquery. For critical queries, I check the generated SQL with toSql() on both databases if we support both. The performance characteristics can be dramatically different.
Source: Laravel Docs (https://laravel.com/docs/eloquent), Laravel News (https://laravel-news.com/), Freek.dev (https://freek.dev/tags/eloquent)