Sei sulla pagina 1di 14

Formularios

Utilizar formularios HTML es una de las ms comunes y desafiantes


tareas para un desarrollador web. Symfony2 integra un
componente Form que se ocupa de facilitarnos la utilizacin de
formularios. En este captulo, construirs un formulario complejo desde
el principio, del cual, de paso, aprenders las caractersticas ms
importantes de la biblioteca de formularios.

Nota
El componente Form de Symfony es una biblioteca independiente que
puedes utilizar fuera de los proyectos Symfony2. Para ms informacin,
consulta el Componente Form de Symfony2 en Github.

Creando un formulario sencillo


Supn que ests construyendo una sencilla aplicacin de tareas
pendientes que necesita mostrar tus pendientes. Debido a que tus
usuarios tendrn que editar y crear tareas, tienes que crear un formulario.
Pero antes de empezar, vamos a concentrarnos en la clase
genrica Task que representa y almacena los datos para una sola tarea:
// src/Acme/TaskBundle/Entity/Task.php
namespace Acme\TaskBundle\Entity;

class Task
{
protected $task;

protected $dueDate;

public function getTask()

{
return $this->task;
}
public function setTask($task)
{
$this->task = $task;
}

public function getDueDate()


{
return $this->dueDate;
}
public function setDueDate(\DateTime $dueDate = null)
{
$this->dueDate = $dueDate;
}
}

Nota
Si ests codificando este ejemplo, primero crea el
paquete AcmeTaskBundle ejecutando la siguiente orden (aceptando todas
las opciones predeterminadas):
$ php app/console generate:bundle --namespace=Acme/TaskBundle

Esta clase es un antiguo objeto PHP sencillo, ya que, hasta ahora, no


tiene nada que ver con Symfony o cualquier otra biblioteca. Es
simplemente un objeto PHPnormal que directamente resuelve un
problema dentro de tu aplicacin (es decir, la necesidad de representar
una tarea pendiente en tu aplicacin). Por supuesto, al final de este

captulo, sers capaz de enviar datos a una instancia de Task (a travs de


un formulario), validar sus datos, y persistirla en una base de datos.

Construyendo el formulario
Ahora que has creado una clase Task, el siguiente paso es crear y
reproducir el formulario HTML real. En Symfony2, esto se hace
construyendo un objeto Form y luego pintndolo en una plantilla. Por
ahora, esto se puede hacer en el interior de un controlador:
// src/Acme/TaskBundle/Controller/DefaultController.php
namespace Acme\TaskBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Acme\TaskBundle\Entity\Task;
use Symfony\Component\HttpFoundation\Request;

class DefaultController extends Controller


{
public function newAction(Request $request)
{
// crea una task y le asigna algunos datos ficticios para este ejemplo
$task = new Task();
$task->setTask('Write a blog post');
$task->setDueDate(new \DateTime('tomorrow'));

$form = $this->createFormBuilder($task)
->add('task', 'text')
->add('dueDate', 'date')

->getForm();

return $this->render('AcmeTaskBundle:Default:new.html.twig', array(


'form' => $form->createView(),
));
}
}

Truco
Este ejemplo muestra cmo crear el formulario directamente en el
controlador. Ms adelante, en la seccin Creando clases Form,
aprenders cmo construir tu formulario en una clase independiente, lo
cual es muy recomendable puesto que vuelve reutilizable tu formulario.
La creacin de un formulario requiere poco cdigo relativamente, porque
los objetosform de Symfony2 se construyen con un generador de
formularios. El propsito del generador de formularios es permitirte
escribir sencillas recetas de formulario, y hacer todo el trabajo pesado
de la contruccin de un formulario.
En este ejemplo, aadiste dos campos al formulario task y dueDate
que corresponden a las propiedades task y dueDate de la clase Task.
Tambin asignaste a cada uno un tipo (por ejemplo, text, date), el cual
entre otras cosas, determina qu etiqueta de formulario HTML se dibuja
para ese campo.
Symfony2 viene con muchos tipos integrados que explicaremos en breve
(consultaTipos de campo integrados).

Reproduciendo el formulario
Ahora que creaste el formulario, el siguiente paso es dibujarlo. Lo puedes
hacer pasando un objeto view especial para formularios a tu plantilla (ten
en cuenta la declaracin $form->createView() en el controlador de arriba)
y usando un conjunto de funciones ayudantes de formulario:

Twig

{# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}
<form action="{{ path('task_new') }}" method="post" {{
form_enctype(form) }}>
{{ form_widget(form) }}

<input type="submit" />

</form>

PHP

<!-- src/Acme/TaskBundle/Resources/views/Default/new.html.php -->

<form action="<?php echo $view['router']->generate('task_new') ?>"


method="post" <?php echo $view['form']->enctype($form) ?> >
<?php echo $view['form']->widget($form) ?>

<input type="submit" />

</form>

Nota
Este ejemplo asume que has creado una ruta llamada task_new que
apunta al controlador AcmeTaskBundle:Default:new creado anteriormente.
Eso es todo! Al imprimir form_widget(form), se pinta cada campo en el
formulario, junto con la etiqueta y un mensaje de error (si lo hay). Tan
fcil como esto, aunque no es muy flexible (todava). Por lo general,
querrs reproducir individualmente cada campo del formulario para que

puedas controlar la apariencia del formulario. Aprenders cmo hacerlo


en la seccin Reproduciendo un formulario en una plantilla.
Antes de continuar, observa cmo el campo de entrada task reproducido
tiene el valor de la propiedad task del objeto $task (es decir, Escribe una
entrada del blog). El primer trabajo de un formulario es: tomar datos de
un objeto y traducirlos a un formato idneo para reproducirlos en un
formulario HTML.

Truco
El sistema de formularios es lo suficientemente inteligente como para
acceder al valor de la propiedad protegida task a travs de los
mtodos getTask() y setTask() de la clase Task. A menos que una propiedad
sea pblica, debe tener mtodos captadores y definidores para que el
componente Form pueda obtener y fijar datos en la propiedad. Para una
propiedad booleana, puedes utilizar un mtodo isser (por es servicio,
por ejemplo, isPublished()) en lugar de un captador (por
ejemplo, getPublished() o getReminder()).
Nuevo en la versin 2.1: La compatibilidad para los mtodos hasser se
aadi enSymfony 2.1.

Procesando el envo del formulario


El segundo trabajo de un formulario es traducir los datos enviados por el
usuario a las propiedades de un objeto. Para lograrlo, los datos
presentados por el usuario deben estar vinculados al formulario. Aade la
siguiente funcionalidad a tu controlador:
// ...
use Symfony\Component\HttpFoundation\Request;

public function newAction(Request $request)


{
// slo configura un objeto $task fresco (remueve los datos de prueba)
$task = new Task();

$form = $this->createFormBuilder($task)
->add('task', 'text')
->add('dueDate', 'date')
->getForm();

if ($request->isMethod('POST')) {
$form->bind($request);

if ($form->isValid()) {
// realiza alguna accin, tal como guardar la tarea en la base de datos

return $this->redirect($this->generateUrl('task_success'));
}
}

// ...
}

Nuevo en la versin 2.1: El mtodo bind se hizo ms flexible en Symfony


2.1. Ahora acepta datos del cliente sin procesar (como antes) o un
objeto Peticin de Symfony. Este es preferible al mtodo
depreciado bindRequest.
Ahora, cuando se presente el formulario, el controlador vincula al
formulario los datos presentados, los cuales se traducen en los nuevos
datos de las propiedades tasky dueDate del objeto $task. Esto sucede a
travs del mtodo bind().

Nota

Tan pronto como se llame a bind(), los datos presentados se transfieren


inmediatamente al objeto subyacente. Esto ocurre independientemente
de si los datos subyacentes son vlidos realmente o no.
Este controlador sigue un patrn comn para el manejo de formularios, y
tiene tres posibles rutas:
1. Inicialmente, cuando se carga el formulario en un navegador, el
mtodo de la peticin es GET, lo cual significa simplemente que se
debe crear y reproducir el formulario;
2. Cuando el usuario enva el formulario (es decir, el mtodo
es POST), pero los datos presentados no son vlidos (la validacin
se trata en la siguiente seccin), el formulario es vinculado y, a
continuacin reproducido, esta vez mostrando todos los errores de
validacin;
3. Cuando el usuario enva el formulario con datos vlidos, el
formulario es vinculado y en ese momento tienes la oportunidad de
realizar algunas acciones usando el objeto $task (por ejemplo,
persistirlo a la base de datos) antes de redirigir al usuario a otra
pgina (por ejemplo, una pgina de agradecimiento o xito).

Nota
Redirigir a un usuario despus de un exitoso envo de formulario evita
que el usuario pueda hacer clic en actualizar y volver a enviar los datos.

Validando formularios
En la seccin anterior, aprendiste cmo se puede presentar un formulario
con datos vlidos o no vlidos. En Symfony2, la validacin se aplica al
objeto subyacente (por ejemplo, Task). En otras palabras, la cuestin no es
si el formulario es vlido, sino ms bien si el objeto $task es vlido
despus de aplicarle los datos enviados en el formulario. Invocar a $form>isValid() es un atajo que pregunta al objeto $task si tiene datos vlidos o
no.
La validacin se realiza aadiendo un conjunto de reglas (llamadas
restricciones) a una clase. Para ver esto en accin, aade restricciones de
validacin para que el campo task no pueda estar vaco y el

campo dueDate no pueda estar vaco y debe ser un


objeto \DateTime vlido.
YAML

# Acme/TaskBundle/Resources/config/validation.yml

Acme\TaskBundle\Entity\Task:

properties:

task:
- NotBlank: ~

dueDate:

- NotBlank: ~

- Type: \DateTime

Annotations

// Acme/TaskBundle/Entity/Task.php

use Symfony\Component\Validator\Constraints as Assert;

class Task

/**

* @Assert\NotBlank()

*/

public $task;

/**

* @Assert\NotBlank()

* @Assert\Type("\DateTime")

*/

protected $dueDate;

XML

<!-- Acme/TaskBundle/Resources/config/validation.xml -->

<class name="Acme\TaskBundle\Entity\Task">
<property name="task">

<constraint name="NotBlank" />

</property>

<property name="dueDate">

<constraint name="NotBlank" />

<constraint name="Type">\DateTime</constraint>
</property>

</class>

PHP

// Acme/TaskBundle/Entity/Task.php

use Symfony\Component\Validator\Mapping\ClassMetadata;

use Symfony\Component\Validator\Constraints\NotBlank;

use Symfony\Component\Validator\Constraints\Type;

class Task

// ...

public static function loadValidatorMetadata(ClassMetadata


$metadata)
{

$metadata->addPropertyConstraint('task', new NotBlank());

$metadata->addPropertyConstraint('dueDate', new NotBlank());

$metadata->addPropertyConstraint('dueDate', new
Type('\DateTime'));
}

Eso es todo! Si vuelves a enviar el formulario con datos no vlidos, vers


replicados los errores correspondientes en el formulario.
Validacin HTML5
A partir de HTML5, muchos navegadores nativamente pueden imponer
ciertas restricciones de validacin en el lado del cliente. La validacin ms
comn se activa al reproducir un atributo required en los campos que son
obligatorios. Para los navegadores compatibles con HTML5, esto se
traducir en un mensaje nativo del navegador que muestra si el usuario
intenta enviar el formulario con ese campo en blanco.
Los formularios generados sacan el mximo provecho de esta nueva
caracterstica aadiendo atributos HTML razonables que desencadenan la
validacin. La validacin del lado del cliente, sin embargo, se puede
desactivar aadiendo el atributo novalidate de la
etiqueta form o formnovalidate a la etiqueta de envo. Esto es
especialmente til cuando deseas probar tus limitaciones en el lado del la
validacin del servidor, pero su navegador las previene, por ejemplo, la
presentacin de campos en blanco.
La validacin es una caracterstica muy poderosa de Symfony2 y tiene su
propiocaptulo dedicado.

Validando grupos

Truco
Si no ests utilizando la validacin de grupos, entonces puedes saltarte
esta seccin.
Si tu objeto aprovecha la validacin de grupos, tendrs que especificar la
validacin de grupos que utiliza tu formulario:
$form = $this->createFormBuilder($users, array(
'validation_groups' => array('registration'),
))->add(...);

Si vas a crear clases form (una buena prctica), entonces tendrs que
agregar lo siguiente al mtodo getDefaultOptions():
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

public function setDefaultOptions(OptionsResolverInterface $resolver)


{
$resolver->setDefaults(array(
'validation_groups' => array('registration')
));
}

En ambos casos, slo se utilizar el grupo de validacin registration para


validar el objeto subyacente.

Grupos basados en datos presentados


Nuevo en la versin 2.1: La posibilidad de especificar una retrollamada o
Cierre envalidation_groups es nueva en la versin 2.1
Si necesitas alguna lgica avanzada para determinar los grupos de
validacin (por ejemplo, basndote en datos presentados), puedes poner
la opcin validation_groupsa un arreglo de retrollamadas, o a un Cierre:

use Symfony\Component\OptionsResolver\OptionsResolverInterface;

public function setDefaultOptions(OptionsResolverInterface $resolver)


{
$resolver->setDefaults(array(
'validation_groups' => array('Acme\\AcmeBundle\\Entity\\Client',
'determineValidationGroups'),
));
}

Esto llamar al mtodo esttico determineValidationGroups() en la


clase Clientedespus de vincular el formulario, pero antes de llevar a cabo
la validacin. El objeto formulario se pasa como argumento al mtodo (ve
el siguiente ejemplo). Adems puedes definir tu lgica completa en lnea
usando un Cierre:
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

public function setDefaultOptions(OptionsResolverInterface $resolver)


{
$resolver->setDefaults(array(
'validation_groups' => function(FormInterface $form) {
$data = $form->getData();
if (Entity\Client::TYPE_PERSON == $data->getType()) {
return array('person');
} else {
return array('company');
}

},
));
}

Tipos de campo integrados

Potrebbero piacerti anche