Catching Laravel Eloquent Exceptions: Easy Peasy Error Messages
Tags: laravel, eloquent, php, webdev
Published: 2019-09-09 09:08:39

---
title: Catching Laravel Eloquent Exceptions: Easy Peasy Error Messages
published: true
cover_image: https://thepracticaldev.s3.amazonaws.com/i/60fbmrt9z9w2k1qahxn4.jpg
description: A quick and easy walkthrough for catching Eloquent errors, and managing and messaging for the front end.
tags: laravel, eloquent, php, webdev
---
# The Problem
I recently found myself in a situation where I had a codebase that would throw exceptions from certain Eloquent queries - but in a *sort of* legitimate way. 

Essentially, the scenario was that certain user accounts did not have relationships with certain models, but were critical for the controller to complete its work.

# The Solution
This sort of thing should normally be dealt with by fixing the issue at the entry point (validation on user creation), however in this particular scenario, it was a much better idea to return an error to the user to let them know that human intervention was required.

So, to solve this, I needed to catch the exception, generate a human-readable message, and send it back to the front end. This functionality should not clutter the controllers, and should be easy to maintain.

Here's my step-by-step approach:

## 1. Set up a trait
Since we probably want this functionality to be available globally through the app, let's set up a trait - which are super useful for this kind of thing.
```php

namespace App\Traits;

Trait GlobalUtility
{
// Code
}
```

## 2. Define the errors
Within the trait, we set up an array of error messages, with a meaningful key name for each. A default message is added to ensure something is always returned.

```php
namespace App\Traits;

Trait GlobalUtility
{
    protected $customExceptionErrors = [
        'generic' => 'Sorry, there was a problem with this page. Please contact your Sales Account Manager for help.',
        'noDealerRelationship' => 'Sorry, your user account does not appear to be connected to a Dealer. Please contact your Sales Account Manager for help.'
    ];
}
```

## 3. Return logic
The return method is a simple check that:
* Accepts a named exception parameter, which is aligned with the keys in the `$customExceptionErrors` array
* Fetches the error message
* Returns the error string or the generic version if none is found (or no parameter is passed)
```php
namespace App\Traits;

Trait GlobalUtility
{
    protected $customExceptionErrors = [
        'generic' => 'Sorry, there was a problem with this page. Please contact your Sales Account Manager for help',
        'noDealerRelationship' => 'Sorry, your user account does not appear to be connected to a Dealer. Please contact your Sales Account Manager for help.'
    ];

    /**
     * Returns a custom error message for the exception.
     *
     * @param string $exceptionName
     * @return string
     */
    public function getExceptionError($exceptionName = 'generic')
    {
        return $this->customExceptionErrors[$exceptionName] ?: $this->customExceptionErrors['generic'];
    }
}
```

## 4. Add Trait and Exception Class to Controller
Next, the trait and exception class are added to the controller where the Eloquent query is being made.

After the namespace declaration, we add:

```php
use App\Traits\GlobalUtility;
use Illuminate\Database\Eloquent\ModelNotFoundException;
```
And within the class:

```php

use GlobalUtility;
```

## 5. Update the Query
Now we must ensure the query utilises a "orFail" method, like `firstOrFail` or `findOrFail`, then pop it into a try & catch blocks.
```php
try 
{
    $dealer = $user->worksFor()->firstOrFail();
}
catch (ModelNotFoundException $e)
{

}
```

## 6. Configure the redirect
Within the catch statement, we add the redirect with a call to the trait method to grab the appropriate error. We'll set this as `customError`, which will be a value available via the Session later on.
```php
try 
{
    $dealer = $user->worksFor()->firstOrFail();
}
catch (ModelNotFoundException $e)
{
    return redirect()->route('orders.history')->with([
        'customError' => $this->getExceptionError('noDealerRelationship')
    ]);
}
```

## 7. Add check and fetch to the Blade file
Finally, we need to ask the app if the `customError` value exists in the session, and if so, display.
```html
{{-- Show custom defined errors (caught from exceptions) if they exist --}}
@if (Session::has('customError'))
    <div class="alert alert-danger">
        <strong>{{ Session::get('customError') }}</strong>
    </div>
@endif
```

And that's it! Hope someone finds this useful :)