Laravel Eloquent: Return a fallback/default value if record not found

Updated: January 17, 2024 By: Guest Contributor Post a comment

Introduction

Laravel’s Eloquent ORM provides an elegant and easy-to-understand interface to interact with the database. Lifestyle of a developer using Laravel can be quite smooth until they encounter missing record errors. In many cases, it is necessary to provide a fallback or default value when a record is not found in the database to maintain application flow and user experience. This tutorial will guide you through the various approaches to returning fallback values from Eloquent queries.

Default Eloquent Behaviour

By default, when you try to retrieve a non-existing record using the findOrFail method, Laravel will throw a ModelNotFoundException, which can be caught and handled accordingly:

$user = User::findOrFail($id);
// If the user with the provided ID does not exist, a ModelNotFoundException is thrown.

Using firstOrNew and firstOrCreate

The firstOrNew and firstOrCreate methods can come in handy when you want to get a fallback value:

// Using firstOrNew
$user = User::where('email', $email)->firstOrNew([]);
// If the user is not found, a new instance of the User model is returned without saving it.

The firstOrCreate method works similarly but saves the newly created model if the lookup results in no records:

// Using firstOrCreate
$user = User::firstOrCreate(['email' => $email], ['name' => 'Default Name']);
// If the user is not found, a new instance is saved with the provided default values.

Using findOrFail with Custom Fallbacks

Sometimes you may need to handle the exception directly and return a custom fallback value:

try {
    $user = User::findOrFail($id);
} catch (ModelNotFoundException $e) {
    $user = new User(['name' => 'Default Name']);
    // Return the user instance with default values
}

Using the COALESCE Function

Another advanced technique is to use raw SQL functions like COALESCE within your Eloquent query to set default values for specific columns. It is particularly useful for read operations where you don’t necessarily want to create a record if it doesn’t exist:

$name = User::select(DB::raw('COALESCE(name, "Default Name") as name'))
    ->where('id', $id)
    ->pluck('name')
    ->first();

Creating a Custom Scope for Fallbacks

For reusable fallback logic, you can define a custom scope in your Eloquent model:

class User extends Model
{
    public function scopeWithFallback($query)
    {
        return $query->select(DB::raw('COALESCE(name, "Default Name") as name'));
    }
}

$name = User::withFallback()->where('id', $id)->pluck('name')->first();

Using Higher Order Messaging

Introduced in Laravel 5.4, higher order messaging can be used to clean up fallbacks:

$user = User::where('id', $id)->firstOr(fn() => new User(['name' => 'Default Name']));

This code will attempt to find a user with the given $id. If no user is found, the firstOr method will call the given Closure, which provides a default User instance.

Using Macros to Extend Eloquent

You can also define macros to add custom functionality to the Eloquent builder. This allows for a convenient way to include fallback mechanisms:

Builder::macro('firstOrNewFallback', function (...$params) {
    return $this->firstOr(...$params, fn() => new User);
});

$user = User::where('email', $email)->firstOrNewFallback([], ['name' => 'Default Name']);

This macro would enable you to provide a closure to determine the fallback behavior for firstOrNewFallback calls.

Conclusion

Throughout this tutorial, we’ve explored multiple techniques for ensuring that a Laravel Eloquent query returns a default value if the queried record does not exist. Whether you are just beginning or are an advanced user, this knowledge will help you write more robust applications by elegantly handling missing data scenarios.