Documentación de Laravel 10.x
Aquí encontrarás fragmentos de código de Laravel y consejos útiles sobre desarrollo web.
Cuando inicias un nuevo proyecto Laravel, el manejo de errores y excepciones ya está configurado para ti. La clase App\Exceptions\Handler
es donde se registran todas las excepciones lanzadas por tu aplicación y luego se representan al usuario. Profundizaremos más en esta clase a lo largo de esta documentación.
La opción debug
en tu archivo de configuración config/app.php
determina cuánta información sobre un error se muestra realmente al usuario. Por defecto, esta opción está configurada para respetar el valor de la variable de entorno APP_DEBUG
, que se almacena en tu archivo .env
.
Durante el desarrollo local, debes establecer la variable de entorno APP_DEBUG
en true
. En tu entorno de producción, este valor siempre debe ser false
. Si el valor se establece en true
en producción, corres el riesgo de exponer valores de configuración sensibles a los usuarios finales de tu aplicación.
Todas las excepciones son manejadas por la clase App\Exceptions\Handler
. Esta clase contiene un método register
donde puedes registrar devoluciones de llamada personalizadas para informe y representación de excepciones. Examinaremos cada uno de estos conceptos en detalle. La notificación de excepciones se utiliza para registrar excepciones o enviarlas a un servicio externo como Flare, Bugsnag o Sentry. Por defecto, las excepciones se registrarán según la configuración de registros. Sin embargo, eres libre de registrar excepciones como desees.
Si necesitas informar diferentes tipos de excepciones de diferentes maneras, puedes usar el método reportable
para registrar un cierre que se debe ejecutar cuando se debe informar de una excepción de un tipo dado. Laravel determinará qué tipo de excepción informa el cierre examinando la sugerencia de tipo del cierre:
use App\Exceptions\InvalidOrderException; /** * Registra las devoluciones de llamada para el manejo de excepciones de la aplicación. */public function register(): void{ $this->reportable(function (InvalidOrderException $e) { // ... });}
Cuando registras una devolución de llamada de informe de excepción personalizada utilizando el método reportable
, Laravel seguirá registrando la excepción utilizando la configuración de registro predeterminada de la aplicación. Si deseas detener la propagación de la excepción en la pila de registro predeterminada, puedes usar el método stop
al definir tu devolución de llamada de informe o devolver false
desde la devolución de llamada:
$this->reportable(function (InvalidOrderException $e) { // ...})->stop(); $this->reportable(function (InvalidOrderException $e) { return false;});
Nota Para personalizar la notificación de excepciones para una excepción dada, también puedes utilizar excepciones reportables.
Si está disponible, Laravel agrega automáticamente el ID del usuario actual a cada mensaje de registro de excepción como datos contextuales. Puedes definir tus propios datos contextuales globales al definir un método context
en la clase App\Exceptions\Handler
de tu aplicación. Esta información se incluirá en cada mensaje de registro de excepción escrito por tu aplicación:
/** * Obtiene las variables de contexto predeterminadas para el registro. * * @return array<string, mixed> */protected function context(): array{ return array_merge(parent::context(), [ 'foo' => 'bar', ]);}
Si bien agregar contexto a cada mensaje de registro puede ser útil, a veces una excepción en particular puede tener un contexto único que te gustaría incluir en tus registros. Al definir un método context
en una de las excepciones de tu aplicación, puedes especificar cualquier dato relevante para esa excepción que deba agregarse a la entrada de registro de la excepción:
<?php namespace App\Exceptions; use Exception; class InvalidOrderException extends Exception{ // ... /** * Obtiene la información de contexto de la excepción. * * @return array<string, mixed> */ public function context(): array { return ['order_id' => $this->orderId]; }}
report
En ocasiones, es posible que necesites informar de una excepción pero continuar manejando la solicitud actual. La función auxiliar report
te permite informar rápidamente de una excepción a través del manejador de excepciones sin renderizar una página de error al usuario:
public function isValid(string $value): bool{ try { // Valida el valor... } catch (Throwable $e) { report($e); return false; }}
Si estás utilizando la función report
en toda tu aplicación, es posible que ocasionalmente informes la misma excepción varias veces, lo que crea entradas duplicadas en tus registros.
Si deseas asegurarte de que solo se informa una sola instancia de una excepción, puedes establecer la propiedad $withoutDuplicates
en true
en la clase App\Exceptions\Handler
de tu aplicación:
namespace App\Exceptions; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; class Handler extends ExceptionHandler{ /** * Indica que una instancia de excepción solo debe informarse una vez. * * @var bool */ protected $withoutDuplicates = true; // ...}
Ahora, cuando la función report
se llama con la misma instancia de una excepción, solo se informará la primera llamada:
$original = new RuntimeException('Whoops!'); report($original); // reported try { throw $original;} catch (Throwable $caught) { report($caught); // ignored} report($original); // ignoredreport($caught); // ignored
Cuando se escriben mensajes en los registros de tu aplicación, los mensajes se escriben en un nivel de registro especificado, que indica la gravedad o importancia del mensaje que se está registrando.
Como se mencionó anteriormente, incluso cuando registras una devolución de llamada de informe de excepción personalizada utilizando el método reportable
, Laravel seguirá registrando la excepción utilizando la configuración de registro predeterminada de la aplicación; sin embargo, dado que el nivel de registro a veces puede influir en los canales en los que se registra un mensaje, es posible que desees configurar el nivel de registro en el que se registran ciertas excepciones.
Para lograr esto, puedes definir una propiedad $levels
en el manejador de excepciones de tu aplicación. Esta propiedad debe contener una matriz de tipos de excepciones y sus niveles de registro asociados:
use PDOException;use Psr\Log\LogLevel; /** * Una lista de tipos de excepciones con sus niveles de registro personalizados correspondientes. * * @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*> */protected $levels = [ PDOException::class => LogLevel::CRITICAL,];
Al construir tu aplicación, habrá algunos tipos de excepciones que nunca querrás informar. Para ignorar estas excepciones, define una propiedad $dontReport
en el manejador de excepciones de tu aplicación. Cualquier clase que agregues a esta propiedad nunca se informará; sin embargo, aún pueden tener lógica de representación personalizada:
use App\Exceptions\InvalidOrderException; /** * Una lista de tipos de excepciones que no se informan. * * @var array<int, class-string<\Throwable>> */protected $dontReport = [ InvalidOrderException::class,];
Internamente, Laravel ya ignora algunos tipos de errores para ti, como excepciones resultantes de errores HTTP 404 o respuestas HTTP 419 generadas por tokens CSRF no válidos. Si deseas indicarle a Laravel que deje de ignorar un tipo dado de excepción, puedes invocar el método stopIgnoring
dentro del método register
de tu manejador de excepciones:
use Symfony\Component\HttpKernel\Exception\HttpException; /** * Registra las devoluciones de llamada para el manejo de excepciones de la aplicación. */public function register(): void{ $this->stopIgnoring(HttpException::class); // ...}
Por defecto, el manejador de excepciones de Laravel convertirá las excepciones en una respuesta HTTP por ti. Sin embargo, eres libre de registrar un cierre de representación personalizado para excepciones de un tipo dado. Puedes lograr esto invocando el método renderable
dentro de tu manejador de excepciones.
El cierre pasado al método renderable
debe devolver una instancia de Illuminate\Http\Response
, que puede generarse mediante la función auxiliar response
. Laravel determinará qué tipo de excepción representa el cierre examinando la sugerencia de tipo del cierre:
use App\Exceptions\InvalidOrderException;use Illuminate\Http\Request; /** * Registra las devoluciones de llamada para el manejo de excepciones de la aplicación. */public function register(): void{ $this->renderable(function (InvalidOrderException $e, Request $request) { return response()->view('errors.invalid-order', [], 500); });}
También puedes usar el método renderable
para anular el comportamiento de representación para excepciones integradas de Laravel o Symfony, como NotFoundHttpException
. Si el cierre dado al método renderable
no devuelve un valor, se utilizará la representación predeterminada de excepciones de Laravel:
use Illuminate\Http\Request;use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Registra las devoluciones de llamada para el manejo de excepciones de la aplicación. */public function register(): void{ $this->renderable(function (NotFoundHttpException $e, Request $request) { if ($request->is('api/*')) { return response()->json([ 'message' => 'Record not found.' ], 404); } });}
En lugar de definir un comportamiento de informe y representación personalizado en el método register
de tu manejador de excepciones, puedes definir los métodos report
y render
directamente en las excepciones de tu aplicación. Cuando estos métodos existen, se llamarán automáticamente por el framework:
<?php namespace App\Exceptions; use Exception;use Illuminate\Http\Request;use Illuminate\Http\Response; class InvalidOrderException extends Exception{ /** * Informa de la excepción. */ public function report(): void { // ... } /** * Representa la excepción en una respuesta HTTP. */ public function render(Request $request): Response { return response(/* ... */); }}
Si tu excepción extiende una excepción que ya es renderizable, como una excepción integrada de Laravel o Symfony, puedes devolver false
desde el método render
de la excepción para representar la respuesta HTTP predeterminada de la excepción:
/** * Representa la excepción en una respuesta HTTP. */public function render(Request $request): Response|bool{ if (/** Determine if the exception needs custom rendering */) { return response(/* ... */); } return false;}
Si tu excepción contiene lógica de informe personalizada que solo es necesaria cuando se cumplen ciertas condiciones, es posible que necesites indicarle a Laravel que a veces informe la excepción utilizando la configuración predeterminada de manejo de excepciones. Para lograr esto, puedes devolver false
desde el método report
de la excepción:
/** * Informa de la excepción. */public function report(): bool{ if (/** Determine if the exception needs custom reporting */) { // ... return true; } return false;}
Nota Puedes hacer una sugerencia de tipo en cualquier dependencia requerida del método
report
, y se inyectarán automáticamente en el método mediante el contenedor de servicios de Laravel.
Si tu aplicación informa un número muy grande de excepciones, es posible que desees limitar la cantidad de excepciones que se registran o se envían a tu servicio externo de seguimiento de errores.
Para tomar una tasa de muestra aleatoria de excepciones, puedes devolver una instancia de Lottery
desde el método throttle
de tu manejador de excepciones. Si la clase App\Exceptions\Handler
no contiene este método, simplemente puedes agregarlo a la clase:
use Illuminate\Support\Lottery;use Throwable; /** * Regula la entrada de excepciones. */protected function throttle(Throwable $e): mixed{ return Lottery::odds(1, 1000);}
También es posible muestrear condicionalmente según el tipo de excepción. Si solo deseas muestrear instancias de una clase de excepción específica, puedes devolver una instancia de Lottery
solo para esa clase:
use App\Exceptions\ApiMonitoringException;use Illuminate\Support\Lottery;use Throwable; /** * Regula la entrada de excepciones. */protected function throttle(Throwable $e): mixed{ if ($e instanceof ApiMonitoringException) { return Lottery::odds(1, 1000); }}
También puedes limitar la frecuencia de las excepciones registradas o enviadas a un servicio externo de seguimiento de errores devolviendo una instancia de Limit
en lugar de una instancia de Lottery
. Esto es útil si deseas protegerte contra ráfagas repentinas de excepciones que inundan tus registros, por ejemplo, cuando un servicio de terceros utilizado por tu aplicación está inactivo:
use Illuminate\Broadcasting\BroadcastException;use Illuminate\Cache\RateLimiting\Limit;use Throwable; /** * Regula la entrada de excepciones. */protected function throttle(Throwable $e): mixed{ if ($e instanceof BroadcastException) { return Limit::perMinute(300); }}
Por defecto, los límites utilizarán la clase de excepción como clave de límite de tasa. Puedes personalizar esto especificando tu propia clave con el método by
en el Limit
:
use Illuminate\Broadcasting\BroadcastException;use Illuminate\Cache\RateLimiting\Limit;use Throwable; /** * Regula la entrada de excepciones. */protected function throttle(Throwable $e): mixed{ if ($e instanceof BroadcastException) { return Limit::perMinute(300)->by($e->getMessage()); }}
Por supuesto, puedes devolver una combinación de instancias de Lottery
y Limit
para diferentes excepciones:
use App\Exceptions\ApiMonitoringException;use Illuminate\Broadcasting\BroadcastException;use Illuminate\Cache\RateLimiting\Limit;use Illuminate\Support\Lottery;use Throwable; /** * Regula la entrada de excepciones. */protected function throttle(Throwable $e): mixed{ return match (true) { $e instanceof BroadcastException => Limit::perMinute(300), $e instanceof ApiMonitoringException => Lottery::odds(1, 1000), default => Limit::none(), };}
Algunas excepciones describen códigos de error HTTP del servidor. Por ejemplo, esto puede ser un error de "página no encontrada" (404), un error "no autorizado" (401) o incluso un error 500 generado por el desarrollador. Para generar dicha respuesta desde cualquier parte de tu aplicación, puedes utilizar la función abort
:
abort(404);
Laravel facilita la visualización de páginas de error personalizadas para varios códigos de estado HTTP. Por ejemplo, para personalizar la página de error para los códigos de estado HTTP 404, crea una plantilla de vista resources/views/errors/404.blade.php
. Esta vista se renderizará para todos los errores 404 generados por tu aplicación. Las vistas dentro de este directorio deben tener el nombre que coincida con el código de estado HTTP al que corresponden. La instancia de Symfony\Component\HttpKernel\Exception\HttpException
generada por la función abort
se pasará a la vista como una variable $exception
:
<h2>{{ $exception->getMessage() }}</h2>
Puedes publicar las plantillas de página de error predeterminadas de Laravel utilizando el comando Artisan vendor:publish
. Una vez que se hayan publicado las plantillas, puedes personalizarlas según tus preferencias:
php artisan vendor:publish --tag=laravel-errors
También puedes definir una página de error "fallback" para una serie de códigos de estado HTTP. Esta página se renderizará si no hay una página correspondiente para el código de estado HTTP específico que ocurrió. Para lograr esto, define una plantilla 4xx.blade.php
y una plantilla 5xx.blade.php
en el directorio resources/views/errors
de tu aplicación.