Laravel Exceptions: How to Catch, Handle and Create Your Own

Quite often web-developers don’t care enough about errors. If something goes wrong, you often see default Laravel texts like “Whoops, something went wrong” or, even worse, the exception code, which is not helpful at all to the visitor. So I decided to write a step-by-step article of how to handle errors in elegant way and present proper error information to the visitor.

As a side-note: this article will also show an example of creating your own Service with Dependency Injection, and handling exceptions thrown by the service.

Preparation: User Search Task

So we have a really simple example – a form to find a user by their ID.

user search by id

We have two routes:

Route::get('/users', 'UserController@index')->name('users.index');
Route::post('/users/search', 'UserController@search')->name('users.search');

And a controller with two methods:

class UserController extends Controller
{

    public function index()
    {
        return view('users.index');
    }

    public function search(Request $request)
    {
        $user = User::find($request->input('user_id'));
        return view('users.search', compact('user'));
    }

}

Finally, resources/views/users/index.blade.php will present a form:

<form action="{{ route('users.search') }}" method="POST">
@csrf
<div class="form-group">
<input id="user_id" class="form-control" name="user_id" type="text" value="{{ old('user_id') }}" placeholder="User ID">
</div>
<input class="btn btn-info" type="submit" value="Search">
</form>

If we search for existing user and it’s found, we see this result:

Screen Shot 2018-04-23 at 8.05.06 AM

It’s all in resources/views/users/search.blade.php:

<h3 class="page-title text-center">User found: {{ $user->name }}</h3>
<b>Email</b>: {{ $user->email }}
<br>
<b>Registered on</b>: {{ $user->created_at }}

Ok, so this is our ideal scenario. But what if the user is not found?


Exception handling

Let’s get out of ideal world. We’re not checking for user existence, we’re only doing this in Controller:

$user = User::find($request->input('user_id'));

And if user is not found, we will see this:

laravel exception

Or, of course, we can set .env file with APP_DEBUG=false and then the browser will just show blank Whoops, looks like something went wrong. But that still doesn’t give any valuable information to our visitor.

Another quick fix we can make is using User::findOrFail() instead of just find() – then if user is not found, Laravel would show 404 page with text “Sorry, the page you are looking for could not be found.”. But this is a default 404 page for the whole project, so not massively helpful to user, is it?

So we need to catch the errors, process them and redirect back to the form with actual understandable error message.

We need to know the exception type and class name that it would return. In case of findOrFail() it would throw an Eloquent exception ModelNotFoundException, so we need to do this:

public function search(Request $request)
{
    try {
        $user = User::findOrFail($request->input('user_id'));
    } catch (ModelNotFoundException $exception) {
        return back()->withError($exception->getMessage())->withInput();
    }
    return view('users.search', compact('user'));
}

Now, let’s actually show an error in Blade:

<h3 class="page-title text-center">Search for user by ID</h3>

@if (session('error'))
<div class="alert alert-danger">{{ session('error') }}</div>
@endif

<form action="{{ route('users.search') }}" method="POST">...</form>

Result:

Screen Shot 2018-04-23 at 8.26.02 AM

Great, we show the error message! But it’s still not ideal, right? Instead of $exception->getMessage() we need to show our own message:

return back()->withError('User not found by ID ' . $request->input('user_id'))->withInput();

Finally!

Screen Shot 2018-04-23 at 8.28.18 AM

Moving Error Message Handling into Service

For now, we’ve taken a really simple example of one action in controller – just finding the user. In real application it gets more complicated – usually controller is calling some kind of external service or package method which may fail with different kind of errors.

Let’s create our own service which would essentially do the same thing, but would throw exception, so controller wouldn’t even have to know the message text.

Let’s move our logic into app/Services/UserService.php:

namespace App\Services;

use App\User;
use Illuminate\Database\Eloquent\ModelNotFoundException;

class UserService
{

    public function search($user_id)
    {
        $user = User::find($user_id);
        if (!$user) {
            throw new ModelNotFoundException('User not found by ID ' . $user_id);
        }
        return $user;
    }

}

And in Controller, we need to call this service. First, we inject it into __construct() method:

use App\Services\UserService;

class UserController extends Controller
{

    private $userService;

    public function __construct(UserService $userService)
    {
        $this->userService = $userService;
    }

// ...

If you are not familiar with dependency injection and how Laravel IOC container works, here’s official documentation or a good article about it.

Now, here’s how our search() method looks:

public function search(Request $request)
{
    try {
        $user = $this->userService->search($request->input('user_id'));
    } catch (ModelNotFoundException $exception) {
        return back()->withError($exception->getMessage())->withInput();
    }
    return view('users.search', compact('user'));
}

Notice that we can use $exception->getMessage() again, and all the error validation or message logic is happening within the service – that’s one of the purpose, to separate these actions, controller shouldn’t perform it.


Step Even Further: Creating Our Own Exception Class

Final chapter in this article – even better architecture when your service throws its own exception related to that particular error, and there could be multiple exception classes depending on error. A good example of such architecture is Stripe library, its usage looks like this:

try {
  // Use Stripe's library to make requests...
} catch(\Stripe\Error\Card $e) {
  // Since it's a decline, \Stripe\Error\Card will be caught
  $body = $e->getJsonBody();
  $err  = $body['error'];

  print('Status is:' . $e->getHttpStatus() . "\n");
  print('Type is:' . $err['type'] . "\n");
  print('Code is:' . $err['code'] . "\n");
  // param is '' in this case
  print('Param is:' . $err['param'] . "\n");
  print('Message is:' . $err['message'] . "\n");
} catch (\Stripe\Error\RateLimit $e) {
  // Too many requests made to the API too quickly
} catch (\Stripe\Error\InvalidRequest $e) {
  // Invalid parameters were supplied to Stripe's API
} catch (\Stripe\Error\Authentication $e) {
  // Authentication with Stripe's API failed
  // (maybe you changed API keys recently)
} catch (\Stripe\Error\ApiConnection $e) {
  // Network communication with Stripe failed
} catch (\Stripe\Error\Base $e) {
  // Display a very generic error to the user, and maybe send
  // yourself an email
} catch (Exception $e) {
  // Something else happened, completely unrelated to Stripe
}

So how can we create our own exception class? Simple, with Artisan command:

php artisan make:exception UserNotFoundException

Here’s what it would generate in app/Exceptions/UserNotFoundException.php:

namespace App\Exceptions;

use Exception;

class UserNotFoundException extends Exception
{
    //
}

Nothing here, right? Let’s fill our exception with some logic.
There could be two methods in this class:

  • report() is used if you want to do some additional logging – send error to BugSnag, email, Slack etc.
  • render() is used if you want to redirect back with error or return HTTP response (like your own Blade file) directly from Exception class

So, for this example we fill our render() method:

namespace App\Exceptions;

use Exception;

class UserNotFoundException extends Exception
{
    /**
     * Report or log an exception.
     *
     * @return void
     */
    public function report()
    {
        \Log::debug('User not found');
    }
}

Finally, this is how we call this exception then, from Controller:

public function search(Request $request)
{
    try {
        $user = $this->userService->search($request->input('user_id'));
    } catch (UserNotFoundException $exception) {
        report($exception);
        return back()->withError($exception->getMessage())->withInput();
    }
    return view('users.search', compact('user'));
}

So, that’s all I wanted to show you about Exception handling and also using Services, as a side-note.

I understand that my example is really simplified, and other people could use Exceptions in a different way, but I hope this article will give an overview of exceptions in general, and reasons WHY you should use them, to show errors to the visitors in elegant way.

More about exceptions and error handling: official Laravel documentation


Update: video example

I’ve recently shot a video for new online-course, which demonstrates some things related to exceptions:

Like our articles?
Check out our Laravel online courses!

10 COMMENTS

  1. Hi,

    nice article, learned a thing or two.

    Just a small note – it’s considered a bad practice to extend your custom exceptions from top level Exception class – it’s advised to rather extend one of lower level ones (http://php.net/manual/en/spl.exceptions.php). In this particular example, I would perhaps extend the ModelNotFoundException itself.

    Also, for anyone interested in topic further, there’s a great video on how to handle “unhappy path” generally – https://www.youtube.com/watch?v=1YAGxJVuuws

    • Thanks for the comment, Peter, really valuable! Yes, I agree, maybe this exception does look like ModelNotFoundException, but the goal was to show how to create custom exceptions in general. Bad example, perhaps.

  2. First off I want to say thank you for taking the time to write this article!
    I have a couple of questions regarding exceptions and the service class.

    The reason why I avoid exceptions is because the try catch statement makes my code harder to read (I know it’s a stupid reason and I understand the importance of exceptions but that’s the number one thing that stops me from using exceptions everywhere), even in your example the search method in the controller got from:

    “`php
    public function search(Request $request)
    {
    $user = User::find($request->input(‘user_id’));
    return view(‘users.search’, compact(‘user’));
    }
    “`

    to

    “`php
    public function search(Request $request)
    {
    try {
    $user = $this->userService->search($request->input(‘user_id’));
    } catch (UserNotFoundException $exception) {
    report($exception);
    return back()->withError($exception->getMessage())->withInput();
    }
    return view(‘users.search’, compact(‘user’));
    }
    “`

    The initial function has 2 lines of code whereas the function that implements the exception handling has 7 lines of code.
    Is there a way to move the try catch block outside of the search method so that the method can remain at 2 lines of code ?

    Another thing that I find unclear is how do I decide which piece of code is best suited for a Service class.
    I usually use Service classes to store uploaded files or manipulate the request data so that I can persist it in the database, I avoid putting database related code in service classes though.
    Is everything a service ?

    • 7 lines of code vs 2 lines of code – there’s actually a way to “hide” it from controller, rendering exceptions in Laravel’s Handler.php.
      And in my case the code seems bigger, in real-life projects is more like 50 lines vs 45 lines – not that significant.

      Service classes and code structure – I’ve analyzed a lot of codebases and concluded that there are hundreds of ways to structure, and everyone does what’s best for them individually. My take on this – is that service should take any logic unrelated to routing (which should be in controller) and model properties (settings, relationships, appends etc).

  3. Hi,
    I am not much familiar with the exception handling so please pardon me if am wrong.
    My doubt is, while using our own Exception Class(UserNotFoundException) how does laravel knows our custom exception is occurred from the following code? Don’t we need to throw it? or laravel automatically find the exception and throws it?

    try {
    $user = $this->userService->search($request->input(‘user_id’));
    } catch (UserNotFoundException $exception) {
    report($exception);
    return back()->withError($exception->getMessage())->withInput();
    }

  4. Hi,
    I am not much familiar with the exception handling so please pardon me if am wrong.
    My doubt is, while using our own Exception Class(UserNotFoundException) how does laravel knows our custom exception is occurred from the following code? Don’t we need to throw it? or laravel automatically find the exception and throws it?

    try {
    $user = $this->userService->search($request->input(‘user_id’));
    } catch (UserNotFoundException $exception) {
    report($exception);
    return back()->withError($exception->getMessage())->withInput();
    }

    • As I can understand from the code above, the user service already throws it

      class UserService
      {

      public function search($user_id)
      {
      $user = User::find($user_id);
      if (!$user) {
      throw new ModelNotFoundException(‘User not found by ID ‘ . $user_id);
      }
      return $user;
      }

      }

      So when you use the user service in your UserController and it can’t find the entry, the user service throws the ModelNotFoundException which you are catching in the UserController

  5. Excellent article, but I have a doubt, is it correct that the service is in charge of showing the view? Would not that be the controller’s job? I speak from ignorance. Greetings.

LEAVE A REPLY

Please enter your comment!
Please enter your name here