In this article, we're going to explore one of the most important and least discussed features of the Laravel web framework—exception handling. Laravel comes with a built-in exception handler that allows you to report and render exceptions easily and in a friendly manner.
In the first half of the article, we'll explore the default settings provided by the exception handler. In fact, we'll go through the default Handler class in the first place to understand how Laravel handles exceptions.
In the second half of the article, we'll go ahead and see how you could create a custom exception handler that allows you to catch custom exceptions.
Setting Up the Prerequisites
Before we go ahead and dive into the Handler class straight away, let's have a look at a couple of important configuration parameters related to exceptions.
Go ahead and open the config/app.php
file. Let's have a close look at the following snippet.
... ... /* |-------------------------------------------------------------------------- | Application Debug Mode |-------------------------------------------------------------------------- | | When your application is in debug mode, detailed error messages with | stack traces will be shown on every error that occurs within your | application. If disabled, a simple generic error page is shown. | */ 'debug' => env('APP_DEBUG', false), ... ...
As the name suggests, if it's set to TRUE
, it'll help you to debug errors that are generated by an application. The default value of this variable is set to a value of the APP_DEBUG
environment variable in the .env
file.
In the development environment, you should set it to TRUE
so that you can easily trace errors and fix them. On the other hand, you want to switch it off in the production environment, and it'll display a generic error page in that case.
In addition to displaying errors, Laravel allows you to log errors in the log file. Let's have a quick look at the options available for logging. Again, let's switch to the config/app.php
file and have a close look at the following snippet.
... ... 'log' => env('APP_LOG', 'single'), 'log_level' => env('APP_LOG_LEVEL', 'debug'), ... ...
As Laravel uses the Monolog PHP library for logging, you should set the above options in the context of that library.
The default log file is located at storage/logs/laravel.log
, and it's sufficient in most cases. On the other hand, the APP_LOG_LEVEL
is set to a value that indicates the severity of errors that'll be logged.
So that was a basic introduction to the configuration options available for exceptions and logging.
Next, let's have a look at the default Handler class that comes with the default Laravel application. Go ahead and open the app/Exceptions/Handler.php
file.
<?php namespace App\Exceptions; use Exception; use Illuminate\Auth\AuthenticationException; 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 = [ \Illuminate\Auth\AuthenticationException::class, \Illuminate\Auth\Access\AuthorizationException::class, \Symfony\Component\HttpKernel\Exception\HttpException::class, \Illuminate\Database\Eloquent\ModelNotFoundException::class, \Illuminate\Session\TokenMismatchException::class, \Illuminate\Validation\ValidationException::class, ]; /** * Report or log an exception. * * This is a great spot to send exceptions to Sentry, Bugsnag, etc. * * @param \Exception $exception * @return void */ public function report(Exception $exception) { parent::report($exception); } /** * Render an exception into an HTTP response. * * @param \Illuminate\Http\Request $request * @param \Exception $exception * @return \Illuminate\Http\Response */ public function render($request, Exception $exception) { return parent::render($request, $exception); } /** * Convert an authentication exception into an unauthenticated response. * * @param \Illuminate\Http\Request $request * @param \Illuminate\Auth\AuthenticationException $exception * @return \Illuminate\Http\Response */ protected function unauthenticated($request, AuthenticationException $exception) { if ($request->expectsJson()) { return response()->json(['error' => 'Unauthenticated.'], 401); } return redirect()->guest(route('login')); } }
There are two important functions that the handler class is responsible for—reporting and rendering all errors.
Let's have a close look at the report
method.
/** * Report or log an exception. * * This is a great spot to send exceptions to Sentry, Bugsnag, etc. * * @param \Exception $exception * @return void */ public function report(Exception $exception) { parent::report($exception); }
The report method is used to log errors to the log file. At the same time, it's also important to note the dontReport
property, which lists all types of exceptions that shouldn't be logged.
Next, let's bring in the render
method.
/** * Render an exception into an HTTP response. * * @param \Illuminate\Http\Request $request * @param \Exception $exception * @return \Illuminate\Http\Response */ public function render($request, Exception $exception) { return parent::render($request, $exception); }
If the report
method is used to log or report errors, the render
method is used to render errors on a screen. In fact, this method handles what will be displayed to users when the exception occurs.
The render
method also allows you to customize a response for different types of exceptions, as we'll see in the next section.
Finally, the unauthenticated
method handles the AuthenticationException
exception that allows you to decide what will be displayed to users in case they're unauthenticated to access a page they are looking for.
Custom Exception Class
In this section, we'll create a custom exception class that handles exceptions of the CustomException
type. The idea behind creating custom exception classes is to easily manage custom exceptions and render custom responses at the same time.
Go ahead and create a file app/Exceptions/CustomException.php
with the following contents.
<?php namespace App\Exceptions; use Exception; class CustomException extends Exception { /** * Report the exception. * * @return void */ public function report() { } /** * Render the exception into an HTTP response. * * @param \Illuminate\Http\Request * @return \Illuminate\Http\Response */ public function render($request) { return response()->view( 'errors.custom', array( 'exception' => $this ) ); } }
The important thing to note here is that the CustomException
class must extend the core Exception
class. For demonstration purposes, we'll only discuss the render method, but of course you could also customize the report method.
As you can see, we're redirecting users to the errors.custom
error page in our case. In that way, you can implement custom error pages for specific types of exceptions.
Of course, we need to create an associated view file at resources/views/errors/custom.blade.php
.
Exception details: <b></b>
That's a pretty simple view file that displays an error message, but of course you could design it the way you want it to be.
We also need to make changes in the render method of the app/Exceptions/Handler.php
file so that our custom exception class can be invoked. Let's replace the render method with the following contents in the app/Exceptions/Handler.php
file.
... ... /** * Render an exception into an HTTP response. * * @param \Illuminate\Http\Request $request * @param \Exception $exception * @return \Illuminate\Http\Response */ public function render($request, Exception $exception) { if ($exception instanceof \App\Exceptions\CustomException) { return $exception->render($request); } return parent::render($request, $exception); } ... ...
As you can see, we are checking the type of an exception in the render method in the first place. If the type of an exception is \App\Exceptions\CustomException
, we call the render method of that class.
So everything is in place now. Next, let's go ahead and create a controller file at app/Http/Controllers/ExceptionController.php
so we can test our custom exception class.
<?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; class ExceptionController extends Controller { public function index() { // something went wrong and you want to throw CustomException throw new \App\Exceptions\CustomException('Something Went Wrong.'); } }
Of course, you need to add an associated route in the routes/web.php
as shown in the following snippet.
// Exception routes Route::get('exception/index', 'ExceptionController@index');
And with that in place, you can run the http://your-laravel-site.com/exception/index URL to see if it works as expected. It should display the errors.custom
view as per our configuration.
So that's how you are supposed to handle custom exceptions in Laravel. And that brings us to the end of this article—I hope you've enjoyed it!
Conclusion
Today, we went through the exception handling feature in Laravel. At the beginning of the article, we explored the basic configuration provided by Laravel in order to render and report exceptions. Further, we had a brief look at the default exception handler class.
In the second half of the article, we prepared a custom exception handler class that demonstrated how you could handle custom exceptions in your application.
For those of you who are either just getting started with Laravel or looking to expand your knowledge, site, or application with extensions, we have a variety of things you can study in Envato Market.
I would love to hear from you in the form of queries and suggestions!
No comments:
Post a Comment