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

viernes, 12 de junio de 2015

Ei! He vuelto

Después de un tiempo desconectado, no sólo del blog sino sobre todo del mundo Grails, vuelvo a conectarme. Ahora toca familiarizarse con la nueva versión de Grails (3.0.1). Así, a primera vista, comienza llamando la atención la ejecución del comando "grails". Se nota que han mejorado la UX de la línea de comandos. Ahora a ver el resto


domingo, 28 de septiembre de 2014

Inicializar la base de datos con un fichero CSV

Uno de los problemas que nos solemos encontrar cuando comenzamos a desarrollar una aplicación, por pequeña que sea, es que necesitamos un conjunto mínimo de datos para trabajar.


Una primera aproximación es aprovechar las características de scaffolding de grails para que, cuando iniciamos la aplicación, introducir estos datos de manera manual a través de los formularios de alta. Pero está claro que esto no es lo que buscamos.

Empecemos por el principio. Supongamos que para nuestra aplicación creamos una clase de dominio para encapsular los datos de personas:


// File: grails-app/domain/test/Person.groovy
package test

class Person {
    String firstName
    String lastName
    int age
}



La solución suele pasar por añadir código al método init() de la clase BootStrap de la aplicación, creando instancias de la clase Person y guardándolas en la base de datos. Esto lo haremos creando el método loadPersonData() en la clase y llamándolo desde init:


// File: grails-app/conf/BootStrap.groovy
import test.Person

class BootStrap {
    def init = { servletContext ->
        environments {
            development {
                if (Person.count() == 0) {
                    loadPersonData()
                }
            }
        }
    }

    def destroy = {
    }

    def loadPersonData() {
        new Person(firstName: 'Albert', lastName: 'Willis', age: 35).save(flush: true)
        new Person(firstName: 'Stephen', lastName: 'West', age: 38).save(flush: true)
        new Person(firstName: 'Raymond', lastName: 'Chavez', age: 32).save(flush: true)
        new Person(firstName: 'Melissa', lastName: 'Rose', age: 25).save(flush: true)
        new Person(firstName: 'Jane', lastName: 'Woods', age: 36).save(flush: true)
        new Person(firstName: 'Michelle', lastName: 'Hansen', age: 33).save(flush: true)
        new Person(firstName: 'Judith', lastName: 'Stewart', age: 28).save(flush: true)
        new Person(firstName: 'Jerry', lastName: 'Carpenter', age: 36).save(flush: true)
        new Person(firstName: 'Debra', lastName: 'Hanson', age: 34).save(flush: true)
        new Person(firstName: 'Larry', lastName: 'Foster', age: 36).save(flush: true)
    }
}


Esta solución nos obliga a copiar los datos dentro del código, lo que puede no interesarnos. Una mejora que haría nuestro código más elegante sería tener un fichero con los datos, por ejemplo, un fichero CSV.


Para continuar, crearemos dentro del directorio "conf/" de nuestro proyecto, un subdirectorio al que llamaremos "resources". Dentro de éste crearemos un fichero "data.csv" con un contenido similar al siguiente:

Albert,Willis,35
Stephen,West,38
Raymond,Chavez,32
Melissa,Rose,25
Jane,Woods,36
Michelle,Hansen,33
Judith,Stewart,28
Jerry,Carpenter,36
Debra,Hanson,34
Larry,Foster,36


De esta forma tendremos los datos que queremos cargar durante el arranque de la aplicación separados del código. Para cargar este fichero, lo primero que tendremos que hacer es añadir a la configuración de la aplicación el plugin CSV para poder tratar ficheros en formato CSV. Esto lo haremos modificando el fichero "conf/BuildConfig.groovy" añadiendo el siguiente código:

// grails-app/conf/BuildConfig.groovy
...
plugins {
    ...
    compile ":csv:0.3.1" 
    ...
}
...

Una vez hecho esto, tendremos que modificar el procedimiento de inicio para que genere las instancias de la clase Person a partir de los datos del fichero CSV. Esto nos obligará a modificar el método loadPersonData. Este método cargará el fichero CSV y por cada registro creará una instancia Person y la guardará en la base de datos.
// File: grails-app/conf/BootStrap.groovy
...
        def loadPersonData() {
            grailsApplication.getParentContext().getResource("classpath:resources/data.csv").inputStream.toCsvReader(['charset':'UTF-8','separatorChar':',']).eachLine { tokens ->
                new Person(firstName: tokens[1], lastName: tokens[2], age: tokens[3]).save(flush: true)
            }
        }
...

Con estos cambios cada vez que se inicie la aplicación cargará los datos del fichero CSV creado.



En una próxima entrada veremos como inicializar la base de datos con un fichero SQL

martes, 2 de septiembre de 2014

Comenzando el curso de jPassion.com

Bueno, pues ya estamos aquí, de nuevo. En este tiempo, entre otras muchas cosas, me he dedicado a buscar algún curso que me permitiera comenzar en el mundo GRAILS (y Groovy) de una manera sencilla.

Sí, ya sé que está el Getting Started, pero bueno, Nunca está de más apoyarte en un curso con un contenido más o menos definido. Así que la semana pasada me decidí, al fin, por registrarme en los cursos de jPassion.com.

Esta web contiene un conjunto de cursos, la mayoría relacionados con el mundo Java, creados e impartidos por un antiguo "evangelizador" de Sun y Java: Sang Shin. Por una subscripción anual, a un precio muy competitivo, tienes acceso a todos los cursos, que van desde una iniciación al java hasta java avanzado o un curso dedicado a la monitorización. También trata frameworks como Spring o Hibernate. Y ya os he hablado de Grails.

Cada curso está dividido en diversas unidades. De cada unidad, los usuarios registrados tienen disponible:
  • Una presentación en formato PDF
  • Un laboratorio, con ejercicios y deberes que el alumno debe hacer por su cuenta
  • Un screencast en el que el propio Sang Shin explica la presentación

En las próximas semanas intentaré ir subiendo aquí algunas ideas y/o trucos que me parezcan interesantes, aunque no podré subir los contenidos del curso, ya que la licencia lo impide.

lunes, 20 de enero de 2014

Bienvenidos a Aprendiendo GRAILS

Hola!

Esta es la primera entrada de mi nuevo blog. Desde hace semanas he comenzado a interesarme por GRAILS como framework de desarrollo de aplicaciones web

A partir de ahora intentaré recoger en este blog aquellas ideas que me vayan surgiendo, referencias a tutoriales, guías, páginas, etc que encuentre interesantes o sobre plugins que vaya utilizando. Además de servir de recopilación de recursos para desarrollo en GRAILS.

Así que si os interesa el tema, espero poder ofreceros contenidos que os puedan servir

Gracias
 
Powered by Blogger
Blogger Templates