En este videotutorial aprenderás los cambios y mejoras al manejo de excepciones a partir de la versión 11 del framework Laravel. Comenzaremos haciendo una comparación breve sobre lo que sucede cuando se arroja una excepción con PHP plano y con el framework Laravel, veremos cómo activar o desactivar el modo de depuración de Laravel, cual es la diferencia entre «reportar» y «renderizar» excepciones y cómo personalizar ambas en Laravel 11.

Además realizaremos un simpático ejemplo con una excepción y una vista personalizadas, para demostrar cómo Laravel nos da total control sobre el manejo y procesamiento de errores de nuestra aplicación, permitiéndonos recibir notificaciones y almacenar toda la información sobre dicho errores para ayudarnos a depurar problemas.

Esta lección incluye un video premium

Regístrate para ver este video y cientos de lecciones exclusivas.

Uso de excepciones en Laravel

Nos ubicamos en routes/web.php para declarar una nueva ruta con la url «exception» que va a lanzar una excepción.

Route::get('exception', function () {
    throw new \Exception('Ha ocurrido un error');
});

En el navegador se podrá visualizar la información de la excepción con cada uno de sus detalles

La información específica de una excepción es delicada, puesto que incluye datos del servidor que no deben ser obtenidos por terceros.

Por lo tanto en producción modificaremos el archivo de entorno .env para desactivar el modo de depuración, evitando de está manera que se exponga información sensible acerca del servidor.

APP_DEBUG=false|

¿Cómo maneja Laravel las excepciones?

Primero reporta al archivo .log ubicado en/storage/logs/laravel.log y luego procede a renderizar como una página de errores o no, dependiendo del modo de depuración que se tenga establecido en la aplicación.

Laravel 10  o inferior

Dentro del directorio /app/Exceptions/ en el archivo Handler.php podíamos personalizar la forma de manejar las excepciones.

Por ejemplo, el nivel de la excepción:

protected $levels = [
];

Las secciones que no se quieren reportar dentro de los detalles de la excepción en el archivo .log:

protected $dontReport
];

Al igual que los campos que no se quiere sean enviados a la sesión si sucede un error:

protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];

En Laravel los errores de validación se manejan por medio de excepciones.

Además, a través del método register() era posible personalizar la forma de reportar y renderizar las excepciones.

public function register(): void
{
     $this->reportable (function (Throwable $e) {
          //
      });
}

Excepciones a partir de Laravel 11

El archivo de Handler.php ya no existe en está nueva versión del framework, por lo que cualquier configuración relacionada con el manejo de excepciones debe realizarse dentro de /bootstrap/app.php

->withExceptions (function (Exceptions $exceptions) {
})->create();

Con el método withExceptions() obtenemos un objeto de configuración que en este caso se encuentra en /Iluminate/Foundation/Configuration/Exceptions.php el cual, en conjunto con los otros objetos de configuración como Midleware nos permiten cambiar la configuración del framework.

Podemos usar este método para configurar la manera en como reportar y renderizar nuestras excepciones.

Ejemplo de configuración de excepciones personalizadas en Laravel 11

Generamos una nueva excepción, haciendo uso del siguiente comando:

php artisan make: exception TeapotException

Esto generará un nuevo directorio app/Exceptions/ con la nueva clase TeapotException que extiende de la clase Exception nativa de PHP.

Y de vuelta en el repositorio /routes/web.php colocamos la nueva excepción.

<?php

Route::get('exception', function () {
    throw new \App\Exceptions\TeapotException('Our server cannot brew coffee because it is a teapot!');
});

A continuación, podemos personalizar la forma como Laravel maneja esta excepción. Por ejemplo, indiquemos que no queremos reportarla:

->withExceptions (function (Exceptions $exceptions) {
    $exceptions->dontReport(\App\Exceptions\TeapotException::class);
})->create();

Personaliza la forma de reportar la excepción

->withExceptions (function (Exceptions $exceptions) {
    $exceptions->report(function \App\Exceptions\TeapotException $exception) {
        // Personaliza la forma cómo Laravel reporta la excepción
    });
})->create();

Haciendo un llamado al método report pasamos una función Callback como primer argumento con el Type Hint de la excepción que queremos personalizar.

El Type Hint es muy importante ya que Laravel utiliza Reflection para entender qué función debe ejecutarse para cada tipo de excepción.

Si queremos agregar el envío de correos como parte de la personalización del manejo de excepciones, necesitamos una clase de tipo Mailable haciendo uso del siguiente comando:

php artisan make:mail TeapotMail

Esto genera un nuevo directorio llamado Mail en el que se encuentra esta clase y donde podemos retornar la vista que deseemos.

De vuelta a bootstrap/app.php llamamos al método Illuminate\Support\Facades\Mail::to('email')->send() dentro de la función  report() para enviar un correo electrónico a la dirección especificada; indicando un destinatario con la propiedad to() y haciendo uso del método  send() pasando el objeto de la clase Mailable que generamos hace un momento:

->withExceptions(function (Exceptions $exceptions) {
    $exceptions->report(function (\App\Exceptions\TeapotException $exception) {
        \Illuminate\Support\Facades\Mail::to('[email protected]')->send(new \App\Mail\TeapotMail());
    });
});

Si durante la ejecución de nuestra aplicación, la excepción TeapotException es arrojada, podremos ver cómo recibimos el correo electrónico.

En el video, he usado Mailtrap para enviar el correo. Puedes aprender más sobre esta sencilla herramienta en nuestra lección sobre Cómo enviar emails de prueba con Mailtrap.io en Laravel.

->withExceptions (function (Exceptions $exceptions) {
    $exceptions->report(function (\App\Exceptions\TeapotException $exception) {
        \Illuminate\Support\Facades\Mail::to('email')->send(new \App\Mail\Teapot Mail());
})->stop();

Si deseamos evitar que además se reporte esta excepción al Log, podemos encadenar el método stop(), luego del llamado del método report().

Podemos obtener el mismo resultado, colocando un return false:

->withExceptions (function (Exceptions $exceptions) {
    $exceptions->report(function (\App\Exceptions\TeapotException $exception) {
        \Illuminate\Support\Facades\Mail::to('[email protected]')->send(new \App\Mail\Teapot Mail());
        return false;
    });
});

Así mismo, podemos seguir personalizando nuestras excepciones, haciendo uso del método render() y, posteriormente, con el método response() retornamos la vista de excepción que deseamos mostrar. Para seguir buenas prácticas, vamos a indicar el código de la respuesta HTTP con setStatusCode:

$exceptions->render(function (\App\Exceptions\TeapotException $exception) {
    return response()->view('errors.teapot')->setStatusCode(418);
});

Por otra parte, es posible definir un nivel predeterminado para ciertas excepciones, según lo consideremos necesario, mediante el método level(). Un ejemplo sería definirlo de tipo CRITICAL ya que, por defecto es ERROR.

<?php
$exceptions->level(\App\Exceptions\TeapotException::class, \Psr\Log\LogLevel::CRITICAL);

En nuestro archivo de Log se identificará el nivel como CRITICAL siendo útil para identificar errores importantes.

Hay excepciones que por defecto no son reportadas por Laravel pero podemos indicarle al framework que las reporte. Un ejemplo es la excepción de validación, utilizando el método stopIgnoring () podemos reportarla fácilmente:

$exceptions->stopIgnoring([
    \Illuminate\Validation\ValidationException::class,
});

Aunque no te recomendaría hacer esto en producción, es solo un simple ejemplo de cuan fácil es personalizar el comportamiento del framework.

Material relacionado

Regístrate hoy en Styde y obtén acceso a todo nuestro contenido.

Lección anterior Middleware en Laravel 11 Lección siguiente Programación de tareas (Schedule) en Laravel 11