Handling Exceptions and Custom Exceptions in Laravel 5.1

Access all tutorials in sprocket icon.

August 19, 2015 from author Bill Keck.

Handling Exceptions & Custom Exceptions in Laravel 5.1

One of the more intimidating aspects of learning programming for me has been throwing exceptions, when to do it, how to do it, etc. They just seem scary for whatever reason, but last night I watched a great video on Laracasts about them.

While the video was very informative, it didn’t show me exactly how to setup a custom exception in Laravel 5.1, so I thought this would make a great tutorial.

Out of the box, Laravel 5.1 comes with an Exceptions folder that sits in the app folder. Within that is the Handler.php file, which we can modify to handle all exceptions, including any custom exceptions we want to throw. This is Handler.php file you get with the standard install:



<?php

namespace App\Exceptions;

use Exception;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;

class Handler extends ExceptionHandler
{
   /**
    * A list of the exception types that should not be reported.
    *
    * @var array
    */
   protected $dontReport = [
       HttpException::class,
   ];

   /**
    * Report or log an exception.
    *
    * This is a great spot to send exceptions to Sentry, Bugsnag, etc.
    *
    * @param  \Exception  $e
    * @return void
    */
   public function report(Exception $e)
   {
       return parent::report($e);
   }

   /**
    * Render an exception into an HTTP response.
    *
    * @param  \Illuminate\Http\Request  $request
    * @param  \Exception  $e
    * @return \Illuminate\Http\Response
    */
   public function render($request, Exception $e)
   {
       return parent::render($request, $e);
   }
}

Probably the most common exception we come across is the ModelNotFoundException, which is going to get thrown if you type an id into the url that doesn’t exist. For example:



yourproject.com/widget/15

If there is no widget record 15, you will get the ModelNotFoundException, which is very helpful to a programmer, but is not what we want the end user to see. So we have to handle this exception when it’s thrown in a way that is digestible to the end user.

To do this, we are going to modify Handler.php to the following:



<?php

namespace App\Exceptions;

use Exception;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Database\Eloquent\ModelNotFoundException;

class Handler extends ExceptionHandler
{
   /**
    * A list of the exception types that should not be reported.
    *
    * @var array
    */
   protected $dontReport = [
       HttpException::class,
   ];

   /**
    * Report or log an exception.
    *
    * This is a great spot to send exceptions to Sentry, Bugsnag, etc.
    *
    * @param  \Exception  $e
    * @return void
    */
   public function report(Exception $e)
   {
       return parent::report($e);
   }

   /**
    * Render an exception into an HTTP response.
    *
    * @param  \Illuminate\Http\Request  $request
    * @param  $e
    * @return \Illuminate\Http\Response
    */
   public function render($request, Exception $e)
   {
       switch($e){

           case ($e instanceof ModelNotFoundException):

               return $this->renderException($e);
               break

           default:

               return parent::render($request, $e);

       }
   }

   protected function renderException($e)
   {

      switch ($e){

          case ($e instanceof ModelNotFoundException):
              return response()->view('errors.404', [], 404);
              break;

          default:
              return (new SymfonyDisplayer(config('app.debug')))
                     ->createResponse($e);

      }

   }


}

You can see that we have set up a switch statement in our render method to determine which kind of exception we are throwing:



 switch($e){

           case ($e instanceof ModelNotFoundException):

               return $this->renderException($e);
               break

           default:

               return parent::render($request, $e);

       }

So we either determine it’s a ModelNotFoundExcpeption or not. The reason I’m using a switch statement here is that I want to make it easy to add other exceptions, which we will show an example of shortly.

If it is a ModelNotFoundExcpeption, then we call the renderException method, which has it’s own switch statement:



switch ($e){

          case ($e instanceof ModelNotFoundException):
              return response()->view('errors.404', [], 404);
              break;

          default:
              return (new SymfonyDisplayer(config('app.debug')))
                     ->createResponse($e);

      }

Obviously, with one exception type, this isn’t really necessary, you could just return the view in the render method. But I’m doing it this way to give myself flexibility as I build out my application.

So now we have a way to handle a ModelNotFoundExcpeption, but we don’t have the corresponding view. So let’s create that now.

Laravel 5.1 comes with an errors folder located in resources/views. Inside the errors folder, create a errors.404.blade.php file with the following contents:



@extends('layouts.master')

@section('content')

//use bootstrap alert div with dismissible

Oh Snap! We can't find what you are looking for...

//end alert div

@endsection

Note: for some reason wordpress has decided to chomp my escape tags, so you are not seeing the HTML for the above. Instead, I’m just including comments for the bootstrap alert div. For your convenience, I’ve linked to the alert markup from the getBootstrap site, so you can just grab it from there.

I have also created a gist so you can get the full code there.

Also note I’m assuming you have a master.blade.php file located in a folder named layouts. If you don’t know what a master page is, please check out my tutorial on Creating A Master Page.

Ok, so that is all you need to run something like:



yourproject.com/widget/78

Assuming there is no widget 78, you will get the nicely formatted exception. You can adjust your error message however you wish.

Ok, so how do we add a custom exception? I don’t have a use case for one, so I’m just going to pick something out of the air and call it ReallyFriendlyException. Let’s create a ReallyFriendlyException.php file in our Exceptions folder with the following contents:



<?php

namespace App\Exceptions;

class ReallyFriendlyException extends \Exception
{


}

Note the namespace. You will have to pull in the correct use statement in our Handler.php file, which can be difficult to debug if you have typos. Just a heads up there.

In this case, we are keeping it super simple, and we don’t even need anything in our class.

Next we need to modify our Handler.php file to the following:



<?php

namespace App\Exceptions;

use Exception;
use App\Exceptions\ReallyFriendlyException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Database\Eloquent\ModelNotFoundException;

class Handler extends ExceptionHandler
{
   /**
    * A list of the exception types that should not be reported.
    *
    * @var array
    */
   protected $dontReport = [
       HttpException::class,
   ];

   /**
    * Report or log an exception.
    *
    * This is a great spot to send exceptions to Sentry, Bugsnag, etc.
    *
    * @param  \Exception  $e
    * @return void
    */
   public function report(Exception $e)
   {
       return parent::report($e);
   }

   /**
    * Render an exception into an HTTP response.
    *
    * @param  \Illuminate\Http\Request  $request
    * @param  $e
    * @return \Illuminate\Http\Response
    */
   public function render($request, Exception $e)
   {
       switch($e){

           case ($e instanceof ModelNotFoundException):

               return $this->renderException($e);
               break;

           case ($e instanceof ReallyFriendlyException):

               return $this->renderException($e);
               break;

           default:

               return parent::render($request, $e);

       }
   }

   protected function renderException($e)
   {

      switch ($e){

          case ($e instanceof ModelNotFoundException):
              return response()->view('errors.404', [], 404);
              break;

          case ($e instanceof ReallyFriendlyException):
              return response()->view('errors.friendly');
              break;
          default:
              return (new SymfonyDisplayer(config('app.debug')))
                     ->createResponse($e);

      }

   }


}

So in the above, we added a case statement in both methods. In the render method, we added:



case ($e instanceof ReallyFriendlyException):

               return $this->renderException($e);
               break;

And in the renderException method, we added:



case ($e instanceof ReallyFriendlyException):
              return response()->view('errors.friendly');
              break;

So you can see how easy it will be to add and handle exception types to our application. We don’t have the view errors.friendly yet, so let’s make an friendly.blade.php file in our errors folder with the following contents:



@extends('layouts.master')

@section('content')

//div for bootstrap dismissable alert

 Oh Snap! You threw a Really Friendly Exception...


//end alert div

@endsection

Again note: For some reason wordpress has decided to chomp my escape tags, so you are not seeing the HTML for the above. Instead, I’m just including comments for the bootstrap alert div. For your convenience, I’ve linked to the alert markup from the getBootstrap site, so you can just grab it from there.

I have also created a gist with the full code.

Now to demonstrate, I’m just going to go to one of my controllers and pop it into the create method, since that is a one line method:



public function create()
{
   throw new ReallyFriendlyException;
   //return view('widget.create');
}

And now we can test it:



yourproject.com/widget/create

And you should get your ReallyFriendlyException. It’s really very simple. Now obviously you could do more with it, optimize the code, etc., but this just gives you an idea of how it all stitches together and how easy it is to create your own custom exceptions in Laravel 5.1.

I hope you have enjoyed this tutorial and found it useful. Click on the sprocket icon at the top of the page to see all tutorials.

Please comment, share, and like if you can, thanks!

I don’t have a donate button, but If you would like to support my work and learn more about Laravel, you can do so by buying one of my books, Laraboot: laravel 5* For Beginners, I really appreciate it.

8 thoughts on “Handling Exceptions and Custom Exceptions in Laravel 5.1

  1. Hello,
    Thank you for the tutorial. I followed your tutorial to create custom exception type but I found I also had to add a using statement for the new exception class in my controller where I was throwing the exception or else it was not found.

    Putting a use statement only in Handler.php didn’t seem to work. Did I miss something or do I have to “Use” it in the controller where I throw that exception?

    Like

  2. Thanks so much.it worked for me. I have used for returning QueryException for unique index for two columns( name, date) on database. now same exception is thrown if there is any other database query related exception . Please help me on this.

    Once again thanks a lot for this clean and effective tutorial.

    Like

  3. Hi Bill, thanks for your tuto, it is very helpful. I just wanted to ask you where the “SymfonyDisplayer” is defined? In the protected function renderException. ” default:
    return (new SymfonyDisplayer(config(‘app.debug’)))
    ->createResponse($e);”

    I am having an issue when I go to my homepage I got this “FatalErrorException …. syntax error, unexpected ‘*’ ”
    You have any idea whats causing this?
    Thanking you in advanced

    Like

    • Well, it’s complaining about a syntax error, so I would check config/app.php in the debug array key, you will see it’s referencing your .env file, so maybe you have a typo somewhere in one of those places. Just a guess…

      Like

Leave a comment