jueves, 9 de julio de 2015

Laravel: findOrFail y errores 404

Logo de Laravel
El método findOrFail() de los modelos de Laravel es muy usado en los controladores ya que nos permite recuperar un registro de un modelo a partir de su ID sin necesidad de comprobar si existe. El resultado es que en caso de no existir, el método findOrFail lanza una excepción ModelNotFoundException.

Así, si tenemos la url "/books/{id}" para mostrar el detalle de un libro, y probamos en nuestro navegador con un ID que no exista, Laravel nos obsequiará con una página de error mostrando la traza del error.

¿Cómo evitarlo?

Bien, lo primero que podemos hacer es capturar la excepción cuando se produzca y abortar la ejecución con un error 404 (página no encontrada):

    public function show($id)
    {
        try {
            $book = Book::findOrFail($id);
        } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
            abort(404);
        }
        return view('books.show', compact('book'));
    }

Además, para que funcione, en nuestro directorio de vistas tendremos el fichero errors/404.blade.php con el HTML que queremos que aparezca en los casos de errores 404

Esta solución tiene un problema evidente: Cada vez que llamemos al método findOrFail de cualquiera de nuestras clases de modelo tendremos que recurrir al bloque try-catch para captura la excepción.

Si lo queremos hacer una única vez, y que sea válido para toda nuestra aplicación, podemos hacerlo a través del manejador de excepciones global de nuestra aplicación web. Este manejador se encuentra en el fichero app/Exceptions/Handler.php. Lo abrimos y comprobamos que tiene un método render con el código siguiente:

    /**
     * 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);
    }

Vemos que lo único que hace es delegar al correspondiente método render() de la clase padre, que es la que forma parte del framework. La aplicación llama a este método cuando salta una excepción y no ha sido capturada por el código. Así, se puede tratar de forma genérica cualquier excepción que se produzca en la aplicación.

En el caso que estamos tratando hoy, queremos que cuando salte una excepción de tipo ModelNotFoundException directamente se muestre la vista de la página de error 404, y además, se devuelva al cliente un código de error 404 indicando que la URL apunta a una página (o recurso) inexistente.

Para ello modificaremos el código del método render para que quede como se muestra a continuación:

    public function render($request, Exception $e)
    {
        // Si la excepción es la que estamos tratando, mostramos una página de error 404:
        if ($e instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
            return response()->view('errors.404', [], 404);
        }
        // Aquí se pueden tratar otras excepciones
        // ...
        // Y si no, pues la salida por defecto:
        return parent::render($request, $e);
    }

De esta manera, cualquier llamada a findOrFail() en la aplicación con un ID de un registro que no exista provocará un salto a la página 404 configurada en la aplicación

 
Powered by Blogger
Blogger Templates