martes, 3 de septiembre de 2013

Lista de tareas simple con Symfony 2: Parte 1

En el tutorial anterior vimos una breve introducción a la estructura de un proyecto Symfony 2. En este y en la siguiente serie de tutoriales vamos a construir una simple lista de tareas con Symfony 2. Iremos explicando paso a paso la creación de una aplicación simple. Y los componentes que la forman.

A la hora de desarrollar una aplicación debemos de tener claro las acciones que va a realizar la misma y mediante que URL's vamos a invocarlas. Ejemplo de URL's que podremos hacer en nuestra aplicación.

URL                                        Action
-------------------------------------------------------------
/                                              Listar tareas (página principal)
/insertar                                   Insertar una tarea
/eliminar/x                                Eliminar la tarea x
/editar/x                                   Editar la tarea x

Creación de la página principal

Configurar la base de datos

Antes de comenzar realmente, tendrás que configurar tu información de conexión a la base de datos. Por convención, esta información se suele configurar en el archivo app/config/parameters.yml aunque también puedes cambiarla desde el navegador entrando en http://localhost/.../web/app_dev.php (esta es la ruta completa si no has configurado el host virtual para el DocumentRoot) y entrando en el apartado configure.

El resultado es un fichero similar a este:

 parameters:  
   database_driver: pdo_mysql  
   database_host: 127.0.0.1  
   database_port: null  
   database_name: tareas  
   database_user: root  
   database_password: tu_password 
   mailer_transport: smtp  
   mailer_host: 127.0.0.1  
   mailer_user: null  
   mailer_password: null  
   locale: en  
   secret: e6ea6da2ce387ebafd55fe3296eeb3a4  
   database_path: null  

Ahora podemos crear la base de datos vacía, sin tablas, con la herramienta app/console de Symfony2:

 php app/console doctrine:database:create  

Y podemos comprobar su creación, por ejemplo con phpMyAdmin.

Implementación del modelo y entidades

Como comentamos en el anterior tutorial, el código del modelo de la aplicación lo vamos a introducir en el directorio /src/Entity/.

La página inicial esta compuesta por un listado de tareas almacenadas previamente en la base datos. Por lo tanto llega el momento de introducir el concepto de ORM. Ya que Simfony 2 utiliza la librería ORM Doctrine 2. Según la wikipedia:  La asignación objeto-relacional (más conocida por su nombre en inglés, Object-Relational mapping, o sus siglas ORM, O/RM, y O/R mapping) es una técnica de programación para convertir datos entre sistemas de tipos incompatibles utilizando lenguajes de programación orientados a objetos”. Esto crea, en efecto, una “base de datos de objetos virtual” que se puede utilizar dentro del lenguaje de programación. En la que las habilidades del ORM traducen desde datos de una base de datos relacional como MySQL en objetos PHP que podemos manipular. Esto nos permite encapsular la funcionalidad que necesitamos en una tabla dentro de una clase.

Por lo tanto es correcto pensar en que cada una de las tablas que crearemos, será encapsulada dentro de una clase. Y cada una de esas clases serán consideradas entidades. En pocas palabras podríamos decir que el objetivo de una entidad es el de contener datos.

Para empezar a trabajar con Doctrine, tenemos que informar a Doctrine 2 cómo debe asignar la entidad (Tarea) a la base de datos. Tal información se especifica en forma de metadatos utilizando las asignaciones de Doctrine 2. Puedes especificar los metadatos en una serie de formatos, incluyendo YAML, PHP, XML y Anotaciones. En esta ocasión usaremos anotaciones. Es importante señalar que no es necesario persistir todas las propiedades de la entidad, por lo tanto no deberás proporcionar metadatos para estas. Esto nos da la flexibilidad de elegir sólo las propiedades que necesitamos que Doctrine 2 asigne a la base de datos. Ya que puede ser útil tener propiedades que utilizaremos como auxiliares para interactuar con los otros elementos, pero que no necesitamos guardar.

Antes de presentar nuestra entidad  vamos a ver unos conceptos de las anotaciones. Mediante estas anotaciones los atributos de nuestra entidad se van a convertir en columnas de nuestra tabla en la base de datos:

- Primero de todo debemos que declarar y asociar la entidad con la tabla que se creará.

  /**   
  * @ORM\Entity   
  * @ORM\Table(name="tarea")   
  */   
  class Tarea {   
  }  

- Declararemos el identificador de la tabla asociada, definiendo que dicha columna contendrá datos de tipo entero y los valores serán auto-incrementados.

  /**   
   * @ORM\Id   
   * @ORM\Column(type="integer")   
   * @ORM\GeneratedValue(strategy="AUTO")   
   */   
   protected $id;   

- Especificaremos el resto de columnas de la tabla asociada, con el nombre, tipo y atributos opcionales dependiendo del tipo de columna definida.

   /**   
   * @ORM\Column(type="string", length=100, nullable=false)   
   */   
   protected $descripcion;     
   /**   
   * @ORM\Column(type="boolean", nullable=false)   
   */   
   protected $estado;   
   /**   
   * @ORM\Column(type="string",columnDefinition="ENUM('baja', 'media','alta')", nullable=false)   
   */   
   protected $prioridad;  

Para obtener más información sobre los tipos de datos disponibles y sus atributos consultar aquí.
En posteriores tutoriales y ejemplo, veremos como relacionar claves ajenas de diferentes tablas.

1. Entidad Tarea

 /*src/Tarea/ListadoBundle/Entity/Tarea.php*/  
 <?php  
 namespace Tarea\ListadoBundle\Entity;  
 use Doctrine\ORM\Mapping as ORM;  
 /**  
  * @ORM\Entity  
  * @ORM\Table(name="tarea")  
  */  
 class Tarea {  
   /**  
    * @ORM\Id  
    * @ORM\Column(type="integer")  
    * @ORM\GeneratedValue(strategy="AUTO")  
    */  
   protected $id;  
   /**  
    * @ORM\Column(type="string", length=100, nullable=false)  
    */  
   protected $descripcion;  
   /**  
    * @ORM\Column(type="date")  
    */  
   protected $fechaRealizar;  
   /**  
    * @ORM\Column(type="boolean", nullable=false)  
    */  
   protected $estado;  
   /**  
   * @ORM\Column(type="string",columnDefinition="ENUM('baja', 'media','alta')", nullable=false)  
   */  
   protected $prioridad;  

Importante: para que Doctrine reconozca el tipo enum hay que indicárselo en  app/config/config.yml

 doctrine:  
   dbal:  
    //...  
     mapping_types:  
       enum: string  

Una utilidad muy útil, sobretodo si es una entidad con numerosas propiedades, es la de poder generar todos los métodos de acceso a las propiedades de las entidades automáticamente. Esto se consigue mediante el siguiente comando.

 $ php app/console doctrine:generate:entities Tarea  

Observemos como ahora la clase/entidad Tarea se ha rellenado con los 'getters' y 'setters' de las distintas propiedades. Muy útil.

Nota: Cada vez que hagas algún cambio en los metadatos ORM de la entidad o añadas nuevos atributos, puedes ejecutar esta orden para generar cualquier captador adicional. Esta orden no hará modificaciones a los métodos de acceso existentes en la entidad, por lo tanto tus métodos de acceso existentes nunca se van a remplazar con esta orden. Esto es importante ya que más tarde puedes personalizar algunos de los métodos de acceso predeterminados.

Ahora ya estamos en disposición de generar la tabla en la base de datos. Y para ello utilizaremos el siguiente comando:

 $ php app/console doctrine:schema:create  

O para actualizar la tabla ya creada si se han modificado la entidad:

 $ php app/console doctrine:schema:update --force  

Esto ejecutará el código SQL necesario para generar el esquema de base de datos para la entidad Tarea. Además le puedes pasar la opción --dump-sql de la tarea para volcar el SQL en lugar de ejecutarlo contra la base de datos. Si ves tu base de datos, notarás que la tabla tarea se ha creado, con los campos que configuraste en la información de asignación. Muy útil también.

2. Fixtures

Antes de pasar al controlador, vamos a ver como introducir datos de prueba mediante el paquete Fixtures de Doctrine. Como no está entre los componentes básicos preinstalados de la edición estandar de Symfony 2.2, vamos a instalarlo mediante Composer.
Para ello iremos al fichero composer.json de la raíz de nuestro proyecto, y en la clave 'require' añadiremos lo siguiente (recuerda que menos el último, cada subelemento de 'require' va separado por una coma).

 "doctrine/doctrine-fixtures-bundle": "dev-master"  

Y finalmente actualizaremos las dependencias (recuerda que composer utiliza git).

 $ composer update  

Si todo ha ido bien, a parte de que composer no habrá dado error, en el directorio  vendor/doctrine/doctrine-fixtures-bundle estará FixturesBundle y solo nos faltará registrar el paquete descargado en app/appKernel.php

 public function registerBundles()  
 {  
   $bundles = array(  
     // ...  
     new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(),  
     // ...  
   );  
   // ...  
 }  

Implementemos una clase con datos de prueba para Tarea. Esta clase la implementaremos dentro de DataFixtures/ORM del Bundle.

 //src/Tarea/ListadoBundle/DataFixtures/ORM/TareaFixtures.php  
 <?php  
 namespace Tarea\ListadoBundle\DataFixtures\ORM;  
 use Doctrine\Common\Persistence\ObjectManager;  
 use Doctrine\Common\DataFixtures\FixtureInterface;  
 use Tarea\ListadoBundle\Entity\Tarea;  
 class TareaFixtures implements FixtureInterface  
 {  
   public function load(ObjectManager $manager)  
   {  
     $Tarea1 = new Tarea();  
     $Tarea1->setDescripcion('Pasear al Yorkie');  
     $Tarea1->setFechaRealizar(new \DateTime('2013-5-1'));  
     $Tarea1->setEstado(false);  
     $Tarea1->setPrioridad('alta');  
     $manager->persist($Tarea1);  
     $Tarea2 = new Tarea();  
     $Tarea2->setDescripcion('Comprar Pan');  
     $Tarea2->setFechaRealizar(new \DateTime('2013-5-2'));  
     $Tarea2->setEstado(false);  
     $Tarea2->setPrioridad('media');  
     $manager->persist($Tarea2);  
     $Tarea3 = new Tarea();  
     $Tarea3->setDescripcion('Hacer footing');  
     $Tarea3->setFechaRealizar(new \DateTime('2013-5-2'));  
     $Tarea3->setEstado(true);  
     $Tarea3->setPrioridad('baja');  
     $manager->persist($Tarea3);  
     $Tarea4 = new Tarea();  
     $Tarea4->setDescripcion('ir al veterinario');  
     $Tarea4->setFechaRealizar(new \DateTime('2013-5-2'));  
     $Tarea4->setEstado(false);  
     $Tarea4->setPrioridad('alta');  
     $manager->persist($Tarea4);  
     $manager->flush();  
   }  
 }  

Comenzaremos creando un objeto (entidad) Tarea con asignado valores a sus atributos gracias a los método 'setters'. En este punto Doctrine 2 no sabe nada del objeto Entidad. Es únicamente cuando hacemos una llamada a $manager->persist($Tarea1) cuando se informa a Doctrine 2 que inicie la gestión de este objeto entidad.
Es importante señalar que si bien Doctrine 2 ya está al tanto del objeto entidad, aún no lo ha guardado en la base de datos. Para ello se requiere una llamada a $manager->flush(). El método flush provoca que Doctrine 2 realmente interactúe con la base de datos y lleve a cabo las acciones en todas las entidades que está gestionando.

Finalmente cargaremos las Fixtures en la base de datos y comprobaremos su correcta inserción:

 php app/console doctrine:fixtures:load  

Este comando borrará la posible información que haya en las tablas relacionadas con las Fixtures cargadas. Si lo que se desea es añadir, hay que usar la opcion --append en el anterior comando.


Entradas relacionadas

Lista de tareas simple con Symfony 2: Parte 2
Lista de tareas simple con Symfony 2: Parte 3
Lista de tareas simple con Symfony 2: Parte 4
Lista de tareas simple con Symfony 2: Parte 5Introducción a Symfony 2
Guía de instalación de un sistema LAMP

No hay comentarios:

Publicar un comentario