How to use exists() vs doesntExist() in Laravel
When building web applications, you constantly need to answer one fundamental question before performing an action: "Does this record exist in the database?"
For example:
- Is this email address already taken by another user?
- Does the user already have an active subscription?
- Are there any unread notifications for this account?
Laravel provides two expressive methods to check for record existence: exists() and doesntExist().
While this sounds straightforward, many developers mistakenly use methods like count() or get() to perform these checks. Doing that can severely hurt your application's performance as your database grows.
Here is a step-by-step guide on how exists() and doesntExist() work under the hood, why they are necessary, and how to use them efficiently.
The Performance Trap (What NOT to Do)
Imagine you want to check if a user has any pending orders before deleting their account.
Bad Approach 1: Using get()
// BAD PERFORMANCE
$pendingOrders = Order::where('user_id', $user->id)
->where('status', 'pending')
->get();
if ($pendingOrders->count() > 0) {
return "Cannot delete account with pending orders.";
}
Why this is bad: get() fetches every single matching row from MySQL into PHP memory, creates Eloquent model objects for all of them, and then counts the array. If the user has 500 orders, you just loaded 500 models into memory just to check if one exists!
Bad Approach 2: Using count()
// BETTER, BUT STILL UNNECESSARY WORK!
$count = Order::where('user_id', $user->id)
->where('status', 'pending')
->count();
if ($count > 0) {
return "Cannot delete account with pending orders.";
}
Why this is bad: count() executes SELECT COUNT(*) FROM orders.... MySQL has to scan the index to count every matching record. If there are 10,000 pending orders in the system, MySQL spends time counting all 10,000, even though you only care if at least one exists.
The Efficient Solution: exists()
The exists() method tells MySQL to stop looking the very second it finds a single matching row.
// FAST, EFFICIENT, AND READABLE
$hasPendingOrders = Order::where('user_id', $user->id)
->where('status', 'pending')
->exists();
if ($hasPendingOrders) {
return "Cannot delete account with pending orders.";
}
What SQL is Executed?
Under the hood, Laravel converts exists() into an extremely lightweight SQL query:
SELECT EXISTS(
SELECT 1
FROM `orders`
WHERE `user_id` = 123 AND `status` = 'pending'
) AS `exists`;
Because MySQL uses EXISTS(), it stops executing as soon as it hits the first match. It returns a simple boolean (true or false) without loading any model objects or scanning unnecessary rows.
Making Code Cleaner: doesntExist()
Often, you want to perform an action when a record does not exist.
Instead of wrapping exists() in an awkward ! (NOT) negation operator, Laravel gives you the doesntExist() method to make your code read like natural English.
// Awkward to read
if (! User::where('email', $email)->exists()) {
// Create user
}
// Clean and readable
if (User::where('email', $email)->doesntExist()) {
// Create user
}
Both methods run the exact same lightweight SQL under the hood; doesntExist() is purely syntax sugar designed to make your code cleaner.
Using exists() with Relationships
You can also chain exists() and doesntExist() directly onto Eloquent relationship methods.
$user = User::find(1);
// Check if this specific user has active subscriptions
if ($user->subscriptions()->where('status', 'active')->exists()) {
// Grant access to premium features
}
// Check if user has no profile photo set
if ($user->photos()->doesntExist()) {
// Show default avatar placeholder
}
Note on Syntax: Notice we used $user->subscriptions() (with parentheses to get the Relationship Builder) instead of $user->subscriptions (without parentheses).
- Using $user->subscriptions() runs a lightweight EXISTS SQL query.
- Using $user->subscriptions loads all related subscription models into memory first.
The thumb rules:
- Do NOT use $query->get()->count() or count($query->get()).
- Do NOT use $query->count() > 0 if you only care about presence.
- DO use $query->exists() or $query->doesntExist() for instant, memory-friendly existence checks.
