Catching Laravel Eloquent Exceptions: Easy Peasy Error Messages

adam_crampton

Adam Crampton

Posted on September 9, 2019

Catching Laravel Eloquent Exceptions: Easy Peasy Error Messages

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.


namespace App\Traits;

Trait GlobalUtility
{
// Code
}
Enter fullscreen mode Exit fullscreen mode

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.

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.'
    ];
}
Enter fullscreen mode Exit fullscreen mode

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)
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'];
    }
}
Enter fullscreen mode Exit fullscreen mode

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:

use App\Traits\GlobalUtility;
use Illuminate\Database\Eloquent\ModelNotFoundException;
Enter fullscreen mode Exit fullscreen mode

And within the class:


use GlobalUtility;
Enter fullscreen mode Exit fullscreen mode

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.

try 
{
    $dealer = $user->worksFor()->firstOrFail();
}
catch (ModelNotFoundException $e)
{

}
Enter fullscreen mode Exit fullscreen mode

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.

try 
{
    $dealer = $user->worksFor()->firstOrFail();
}
catch (ModelNotFoundException $e)
{
    return redirect()->route('orders.history')->with([
        'customError' => $this->getExceptionError('noDealerRelationship')
    ]);
}
Enter fullscreen mode Exit fullscreen mode

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.

{{-- 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
Enter fullscreen mode Exit fullscreen mode

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

💖 💪 🙅 🚩
adam_crampton
Adam Crampton

Posted on September 9, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related