jueves, 5 de septiembre de 2013

Lista de tareas simple con Symfony 2: Parte 4

Página Insertar Tarea

Primero de todo vamos a crear una nueva entrada en la configuración de enrutamiento:

 #src/Tarea/ListadoBundle/resources/config/routing.yml  
 ...  
 insertar:  
   pattern: /insertar  
   defaults: { _controller: TareaListadoBundle:Tarea:create }  
   requirements:  { _method: "GET|POST" }  

Podemos ver que la acción se llamará cuando la url del navegador se corresponda con localhost/../insertar y que vamos a usar el mismo controlador (TareaController) pero esta vez llamando al método create.
Es importante especificar que la página podrá ser llamada mediante GET para acceder al formulario y mediante POST, cuando el formulario envíe datos al servidor. Por lo tanto hay que especificar ambos métodos en los requisitos.

A continuación vamos a crear el formulario de inserción de una tarea. Symfony 2 viene incluye un componente para formularios muy potente que facilita la tediosa tarea de tratar con formularios. Y recuerda que, como ocurre con todos los componentes de Symfony2, lo puedes utilizar fuera de Symfony2 en tus propios proyectos.

1. Formulario

Vamos a empezar creando una clase TareaType que representa el formulario de la consulta (en /Form/ del Bundle). Podríamos haber creado el formulario directamente en el controlador y no molestarnos con esta clase, sin embargo, separar el formulario en su propia clase nos permite volver a utilizar el formulario a través de la aplicación. También nos evita saturar el controlador. Después de todo, se supone que el controlador es simple. Su propósito es proporcionar el pegamento entre el Modelo y la Vista.

La clase TareaType introduce la clase FormBuilderInterface. Esta es capaz de simplificar el proceso de definición de campos basándose en los metadatos con que cuenta el campo. Por omisión tienen el tipo de campo de entrada de texto. Puedes aprovechar los nuevos tipos de entrada de HTML5 (email,date....).
Esto quiere decir que con Symfony 2 un formulario no será definido creando elementos HTML. Ya que podemos asociar un formulario a una entidad. De esta forma, añadiremos al objeto del tipo FormBuilderInterface los campos de la entidad que queremos que aparezcan como elementos del formulario. Especificando el tipo de elemento de formulario y sus posibles opciones.Y Symfony ya se encarga de renderizar el formulario.

 /*src/Tarea/ListadoTareaBundle/Form/TareaType.php*/  
 <?php  
 namespace Tarea\ListadoBundle\Form;  
 use Symfony\Component\Form\AbstractType;  
 use Symfony\Component\Form\FormBuilderInterface;   
 class TareaType extends AbstractType  
 {  
   public function buildForm(FormBuilderInterface $builder, array $options)  
   {  
     $builder->add('descripcion','textarea');  
     $builder->add('fechaRealizar', 'date');  
     $builder->add('estado', 'checkbox', array(  
       'label'   => '¿Se ha realizado la tarea?',  
       'required' => false));  
     $builder->add('prioridad', 'choice', array(  
       'choices' => array('alta' => 'alta', 'media' => 'media', 'baja' => 'baja')));  
   }  
   public function getName()  
   {  
     return 'Tarea';  
   }  
 }  

El tipo de datos 'choice' servirá para crear un elemento en el que haya opciones para seleccionar. Como podría ser un Select. Para ver los tipos de datos y opciones disponibles ir a la documentación oficial.

2. Controlador y formulario

Después de crear la clase del formulario vamos a crear dicho formulario en el controlador. Tras importar el espacio de nombres necesario, empezamos creando una instancia de la entidad  Tarea. Esta entidad representa los datos de una consulta de tarea. Evidentemente como acabamos de crear el objeto tarea, este se encontrará vació. Pero ya puedes imaginarte que para la funcionalidad de editar una tarea, tendremos que pasar una tarea concreta a un formulario.
A continuación, creamos el formulario real. Especificamos el TareaType que creamos antes, y le pasamos nuestro objeto entidad Tarea. El método createForm es capaz de utilizar estos indicios (objeto formulario y entidad que representa el formulario) para crear una representación del formulario. De momento no haremos ninguna comprobación  de datos y pasamos directamente a la vista.

 /*src/Tares/TareaBundle/Controller/TareaController.php*/  
 <?php  
 namespace Tarea\ListadoBundle\Controller;  
 use Symfony\Bundle\FrameworkBundle\Controller\Controller;  
 // Importa el nuevo espacio de nombres  
 use Tarea\ListadoBundle\Entity\Tarea;  
 use Tarea\ListadoBundle\Form\TareaType;  
 class TareaController extends Controller {  
   public function indexAction() {  
      //getEntityManager lo podemos sustituir por getManager debido ha en la versión  
      //2.3 será sustituido  
     $em = $this->getDoctrine()  
         ->getEntityManager();  
     $tareas = $em->getRepository('TareaListadoBundle:Tarea')  
         ->getTareas();  
     return $this->render('TareaListadoBundle:Tarea:listado.html.twig', array(  
           'tareas' => $tareas  
     ));  
     //print_r($tareas);  
   }  
   public function createAction() {  
     $tarea = new Tarea();  
     $form = $this->createForm(new TareaType(),$tarea);  
     $request = $this->getRequest();  
     return $this->render('TareaListadoBundle:Tarea:nueva.html.twig', array(  
           'form' => $form->createView()  
     ));  
   }  
 }  

Como la entidad que hemos proporcionado al formulario esta vacia, el formulario que hemos creado se mostrará sin datos.

3. Vista del formulario

Como dijimos en anteriores tutoriales, es aconsejable crear subdirectorios, dentro de Resources/views/ con el nombre del controlador que se encarga de renderizar la vista. Así que vamos a crear la vista, que hereda de la vista base, para muestra el formulario de la nueva tarea.
Parece evidente que para las funcionalidades de crear nueva tarea y editar una tarea necesitaremos usar el mismo formulario. Por lo tanto vamos a crear el formulario en una plantilla que será incluida y reutilizada tanto en la vista nueva tarea como en la vista de editar (que veremos más adelante). Además vamos a pasar la variable 'form' que contendrá las representaciones de los elementos del formulario (form->createView) pasadas desde el anterior controlador.

 {% extends 'TareaListadoBundle::layout.html.twig' %}  
 {% block stylesheets %}  
  {{ parent() }}  
 <link rel="stylesheet" href="{{ asset('bundles/tarealistado/css/nueva.css') }}" type="text/css" media="all" />  
 {% endblock %}  
 {% block content %}  
   <div id="form_container">  
     <div id="titulo">  
       <span>Nueva Tarea</span>  
     </div>  
     <form action="{{ path('insertar') }}" method="post" {{ form_enctype(form) }}>  
       {% include 'TareaListadoBundle:Tarea:form.html.twig' with { 'form': form} %}  
     </form>  
   </div>  
 {% endblock %}  

El método form_enctype establece el tipo de contenido del formulario. Este se debe establecer cuando tu formulario trata con la subida de archivos. Nuestro formulario no tiene ningún uso para este método, pero es buena práctica utilizarlo siempre en todos tus formularios en caso de que puedas agregar la carga de archivos en el futuro.

Para mostrar un formulario tenemos diversas opciones dependiendo de las funciones de Twig que utilicemos para mostrar información de formularios. Cada una de estas funciones admite como parámetro tanto un formulario (colección de elementos) o un elemento individual del formulario.

       {# opcion 1   
       {{ form_errors(form) }}  
       {{ form_row(form.descripcion) }}  
       {{ form_row(form.fechaRealizar) }}
       ...  
       {{ form_rest(form) }}  
       #}  

- El método form_errors reproducirá cualquier error sobre el parámetro proporcionado (form en este caso) en caso de que la validación haya fallado.
- El método form_row reproduce elementos indicados, relacionados con cada campo del formulario. Esto incluye los errores del campo, la etiqueta label (por defecto por Symfony) para el campo y el elemento gráfico real del campo (html).
- El método form_rest siempre es una apuesta segura a utilizar como método final del formulario para reproducir los campos que puedas haber olvidado, incluidos los campos ocultos y el segmento CSRF de los formularios de Symfony2.

       {#opcion 2   
       {{ form_errors(form) }}  
       {{ form_widget(form) }}  
       {{ form_rest(form) }}  
       #}  

- El método form_widget renderizará el elemento pasado como parámetro (html). En esta ocasión se ha pasado 'form' por lo que renderizará cada uno de los elementos del formulario. A diferencia del método form_row, solo renderíza los elementos del formulario y no los errores o etiquetas (labels).

Evidentemente las versiones anteriores no son todo lo completas que nos gustaría por eso va a ver la tercera opción probada para mostrar nuestro formulario. Además es la elegida.


       {#opcion 3   
       <div>  
         {{ form_label(form.descripcion,'Descripción de la tarea') }}  
         <span class="error">{{ form_errors(form.descripcion) }}</span>  
         {{ form_widget(form.descripcion) }}  
       </div>  
       <div>  
         {{ form_label(form.fechaRealizar,'Fecha límite') }}  
          <span class="error">{{ form_errors(form.fechaRealizar) }}</span>  
         {{ form_widget(form.fechaRealizar) }}  
       </div>  
       <div>  
         {{ form_label(form.estado,'Tarea completada') }}  
          <span class="error">{{ form_errors(form.estado) }}</span>  
         {{ form_widget(form.estado) }}  
       </div>  
       <div>  
         {{ form_label(form.prioridad,'Prioridad de la tarea') }}  
          <span class="error">{{ form_errors(form.prioridad) }}</span>  
         {{ form_widget(form.prioridad) }}  
       </div>  
       {{form_rest(form)}}    
       #}  

- El método form_label permite mostrar el label asociado al elemento del formulario pasado como parámetro. Por defecto el label es igual al nombre único especificado en la definición de formulario. Por lo que en las opciones (segundo parámetro) del método podremos especificar otro label.
- En esta ocasión, al método form_errors le pasamos como parámetro un elemento concreto. De está forma obtendremos el error concreto para dicho elemento.
- Ahora a form_widget le hemos pasado, cada vez, un elemento individual a renderizar. En la opción 2 a este método se le pasó todo la colección de elementos.

CSRF (Cross-site request forgery) es un método por el cual un usuario malintencionado intenta hacer que usuarios legítimos, sin saberlo, presenten datos que no tienen la intención de enviar. Afortunadamente, los ataques CSRF se pueden prevenir usando un elemento CSRF dentro de tus formularios.
Por defecto, Symfony 2 integra y valida elementos CSRF automáticamente. Esto significa que puedes aprovechar la protección CSRF sin hacer nada. La protección CSRF funciona añadiendo un campo oculto al formulario, por defecto denominado _token, el cual contiene un valor que sólo el servidor y el usuario conocen. Esto garantiza que el usuario es el que presenta dichos datos. Symfony 2 automáticamente valida la presencia y exactitud de este elemento.

El elemento CSRF se puede personalizar formulario por formulario. Para modificaremos las opciones del formulario añadiendo el método setDefaultOptions en la clase TareaType. Por ejemplo:

/*src/Tarea/ListadoTareaBundle/Form/TareaType.php*/ 
use Symfony\Component\OptionsResolver\OptionsResolverInterface;  
 class TareaType extends AbstractType  
 {  
   // …  
   public function setDefaultOptions(OptionsResolverInterface $resolver)  
   {  
     $resolver->setDefaults(array(  
       'data_class'   => 'Tarea\ListadoBundle\Entity\Tarea',  
       'csrf_protection' => true,  
       'csrf_field_name' => '_token',  
       // una clave única para ayudar generar la clave  
       'intention'    => 'task_item',  
     ));  
   }  

Para desactivar la protección CSRF, fija la opción csrf_protection a false. Las personalizaciones también se pueden hacer a nivel global en tu proyecto.

Versión final de form.html.twig

 {# src/Tarea/ListadoTareaBundle/Resources/views/Tarea/form.html.twig #}   
       <div>  
         {{ form_label(form.descripcion,'Descripción de la tarea') }}  
         <span class="error">{{ form_errors(form.descripcion) }}</span>  
         {{ form_widget(form.descripcion) }}  
       </div>  
       <div>  
         {{ form_label(form.fechaRealizar,'Fecha límite') }}  
          <span class="error">{{ form_errors(form.fechaRealizar) }}</span>  
         {{ form_widget(form.fechaRealizar) }}  
       </div>  
       <div>  
         {{ form_label(form.estado,'Tarea completada') }}  
          <span class="error">{{ form_errors(form.estado) }}</span>  
         {{ form_widget(form.estado) }}  
       </div>  
       <div>  
         {{ form_label(form.prioridad,'Prioridad de la tarea') }}  
          <span class="error">{{ form_errors(form.prioridad) }}</span>  
         {{ form_widget(form.prioridad) }}  
       </div>  
       {{form_rest(form)}}  
       <div>  
         <input type="submit" value="Enviar">  
       </div>  

4. Estilo del la vista del formulario

 /* src/Tarea/ListadoBundle/Resources/public/css/nueva.css */  
 #form_container{  
   margin:20px 0;  
 }  
 #form_container form{  
   background-color: #eee;  
   border-radius: 0px 0px 5px 5px;  
   padding:15px  
 }  
 label{  
   color:#444;  
 }  
 select{  
   padding: 0 20px;  
 }  
 #Tarea_descripcion{  
   width: 500px;   
   height: 100px;  
 }  
 #Tarea_descripcion, #Tarea_fechaRealizar, #Tarea_prioridad{  
   display: block;  
   margin: 10px 0 20px 0;  
 }  
 #Tarea_estado{  
   margin: 0px 0 20px 0;  
 }  
 input[type="submit"]{  
   margin-top: 20px;  
   padding: 5px 10px;  
   background: #2daebf;  
   border-radius: 5px;  
   border:0px;  
   color: #fff;  
   text-decoration: none;  
   font-weight: bold;  
   line-height: 1;  
   -moz-border-radius: 5px;  
   -webkit-border-radius: 5px;  
   -moz-box-shadow: 0 1px 3px rgba(0,0,0,0.5);  
   -webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.5);  
   text-shadow: 0 -1px 1px rgba(0,0,0,0.25);  
   border-bottom: 1px solid rgba(0,0,0,0.25);  
   position: relative;  
   cursor: pointer;  
 }  
 input[type="submit"]:hover{background-color:#513f33;}  
 span.error{  
   color: red;  
   font-weight: bolder;  
 }  

En el siguiente tutorial vamos a ver como guardar la tarea introducida en el formulario y a validar la información introducida.


Entradas relacionadas

Lista de tareas simple con Symfony 2: Parte 1
Lista de tareas simple con Symfony 2: Parte 2
Lista de tareas simple con Symfony 2: Parte 3
Introducción a Symfony 2
Seguridad PHP - Cross Site Request Forgery (CSRF)

No hay comentarios:

Publicar un comentario