Formularios Symphony

167
FORMULARIOS SYMPHONY Capítulo 1. Creación de for mularios Los formularios están formados por campos de diferentes tipos: campos ocultos ( hidden), cuadros de texto (input text), listas desplegables (select), cuadros de selección (checkbox), etc. Este capítulo explica cómo crear formularios y cómo tratar sus campos con el framework de formulari os de S ymf ony. Para seguir los capítulos de este libro es necesario conocer Symfony 1.2 y tenerlo correctamente instalado y configurado. También debes crear un proyecto nuevo y una aplicación llamada frontend para seguir todos los ejemplos. Puedes consultar el libro oficial de Symfony 1.2 si tienes alguna duda sobre có mo crear el proyecto y la aplicac ión. 1.1. Antes de empezar Vamos a empezar añadiendo un formulario de contacto a una aplicación Symfony. La figura 1-1 muestra el formulario de contacto t al y como lo ven los usuarios que qu ieren enviar un mensaje. Figura 1.1. Formulario de contacto El formulario dispone de tres campos: el no mbre del usuario, el email del usuario y el mensaje que el usuario quiere env iar. Como respuesta al envío del for mulari o, en este  primer ejemplo simplemente se va a mostrar toda la información e nviada por el usuario, tal y como se muestra en la figura 1-2.

Transcript of Formularios Symphony

Page 1: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 1/167

 

FORMULARIOS SYMPHONY

Capítulo 1. Creación de formularios

Los formularios están formados por campos de diferentes tipos: campos ocultos (hidden),

cuadros de texto (input text), listas desplegables (select), cuadros de selección

(checkbox), etc. Este capítulo explica cómo crear formularios y cómo tratar sus camposcon el framework de formularios de Symfony.

Para seguir los capítulos de este libro es necesario conocer Symfony 1.2 y tenerlocorrectamente instalado y configurado. También debes crear un proyecto nuevo y una

aplicación llamada frontend para seguir todos los ejemplos. Puedes consultar el librooficial de Symfony 1.2 si tienes alguna duda sobre cómo crear el proyecto y la aplicación.

1.1. Antes de empezar

Vamos a empezar añadiendo un formulario de contacto a una aplicación Symfony. La

figura 1-1 muestra el formulario de contacto tal y como lo ven los usuarios que quierenenviar un mensaje.

Figura 1.1. Formulario de contacto

El formulario dispone de tres campos: el nombre del usuario, el email del usuario y el

mensaje que el usuario quiere enviar. Como respuesta al envío del formulario, en este primer ejemplo simplemente se va a mostrar toda la información enviada por el usuario, taly como se muestra en la figura 1-2.

Page 2: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 2/167

 

Figura 1.2. Página de agradecimiento como respuesta al envío del formulario

La figura 1-3 muestra la interacción completa de la aplicación con el usuario, desde que se

muestra el formulario hasta que se visualiza la página de respuesta.

Figura 1.3. Interacción con el usuario

1.2. Widgets

1.2.1. Las clases sfForm y sfWidget 

Los usuarios introducen la información en los campos de los formularios. En Symfony un

formulario es un objeto que hereda de la clase sfForm. En nuestro ejemplo vamos a crear 

una clase llamada ContactoForm y que hereda de la clase sfForm.

Nota 

sfForm es la clase base de todos los formularios y simplifica la gestión de su flujo de

trabajo.

Para empezar a configurar el formulario, se añaden widgets mediante el método

configure().

Un widget representa un campo del formulario. En nuestro ejemplo tenemos que añadir tres

widgets, que son los tres campos del formulario: nombre, email y mensaje. El listado 1-1

muestra la primera versión de la clase ContactoForm.

Listado 1-1 - La clase ContactoForm  con tres campos 

// lib/form/ContactoForm.class.phpclass ContactoForm extends sfForm{publicfunction configure(){$this->setWidgets(array('nombre' =>new sfWidgetFormInput(),'email' =>new sfWidgetFormInput(),

Page 3: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 3/167

 

'mensaje' =>new sfWidgetFormTextarea(),));}}

Los widgets se definen en el método configure(). Este método se invoca

automáticamente desde el constructor de la clase sfForm.

El método setWidgets() se utiliza para definir los widgets del formulario. Este métodoacepta como parámetro un array asociativo en el que las claves son los nombres de los

campos y los valores son los objetos de tipo widget. Cada widget es un objeto que hereda

de la clase sfWidget. En el ejemplo anterior se han utilizado dos tipos de widgets:

y  sfWidgetFormInput: este widget representa un campo de tipo<input> 

y  sfWidgetFormTextarea: este widget representa un campo de tipo<textarea> 

Nota 

Las clases de los formularios se guardan por convención en el directorio lib/form/. Noobstante, puedes guardar los formularios en cualquier otro directorio incluido en el

mecanismo de carga automática de clases de Symfony. Más adelante se verá que el propio

framework utiliza el directorio lib/form/ para generar automáticamente los formularios a partir de los objetos del modelo.

1.2.2. Visualizando el formulario

 Nuestro formulario ya está listo para ser utilizado. A continuación se crea un módulo en la

aplicación para visualizar el formulario:

$ cd /ruta/hasta/el/proyecto$ php symfony generate:module frontend contacto

En el módulo contacto se modifica la acción index para pasar una instancia del

formulario a la plantilla, tal y como se muestra en el listado 1-2.

Listado 1-2 - La clase de las acciones del módulo contacto 

// apps/frontend/modules/contacto/actions/actions.class.phpclass contactoActions extends sfActions{publicfunction executeIndex(){$this->formulario = new ContactoForm();}}

Al crear un formulario mediante new ContactoForm(), se invoca automáticamente el

método configure() definido anteriormente.

Page 4: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 4/167

 

Ahora ya sólo es necesario crear una plantilla como la del listado 1-3 para visualizar el

formulario.

Listado 1-3 - La plantilla utilizada para visualizar el formulario 

// apps/frontend/modules/contacto/templates/indexSuccess.php<form action="<?php echo url_for('contacto/enviar') ?>" method="POST"><table><?phpecho$formulario?><tr><td colspan="2"><input type="submit" /></td></tr></table></form>

Los formularios de Symfony sólo se encargan de los widgets que muestran la información a

los usuarios. En la plantilla indexSuccess, la línea <?php echo $formulario ?> sólomuestra los tres campos del formulario. El resto de elementos, como la etiqueta <form> y el botón de envío, los debe añadir el programador. Aunque este comportamiento puede parecer poco intuitivo al principio, pronto se verá lo útil y sencillo que es para crear 

formularios más complejos.

Utilizar la instrucción <?php echo $formulario ?> es muy útil para crear prototipos ydefinir formularios. Permite a los programadores concentrarse en la lógica de la aplicaciónsin preocuparse de los detalles gráficos. El capítulo tres explica cómo personalizar la

 plantilla y el diseño del formulario.

Nota 

Cuando se muestra un objecto usando <?php echo $formulario ?>, el intérprete de PHP

muestra la representación en texto del objeto $formulario. Para convertir el objeto en una

cadena de texto, PHP intenta ejecutar el método mágico __toString(). Todos los widgetsimplementan este método para convertir el objeto en código HTML. Por lo tanto, ejecutar 

<?php echo $formulario ?> es equivalente a ejecutar <?php echo $formulario-

>__toString() ?>.

Ahora ya es posible visualizar el formulario en un navegador (figura 1-4) y comprobar el

resultado accediendo a la dirección de la acción contacto/index 

(/frontend_dev.php/contacto).

Page 5: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 5/167

 

 

Figura 1.4. Formulario de contacto generado

A continuación se muestra el código HTML generado por la plantilla:

Listado 1-4 Código HTML generado por la plantilla 

<formaction="/frontend_dev.php/contacto/enviar"method="POST"><table>

<!-- Código generado por <?php echo $formulario ?>--><tr><th><labelfor="nombre">Nombre</label></th><td><inputtype="text"name="nombre"id="nombre" /></td></tr>

<tr><th><labelfor="email">Email</label></th><td><inputtype="text"name="email"id="email" /></td></tr><tr><th><labelfor="mensaje">Mensaje</label></th><td><textarearows="4"cols="30"name="mensaje"id="mensaje"></textarea></td></tr><!-- Fin del código generado por <?php echo $formulario ?>-->

<tr><tdcolspan="2"><inputtype="submit" />

</td></tr></table></form>

El código HTML generado por el formulario está compuesto de tres filas de tabla (etiqueta

<tr>). Por ese motivo el código de la plantilla utilizaba antes una etiqueta <table> para

Page 6: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 6/167

 

encerrar los contenidos del formulario. Cada fila de formulario incluye una etiqueta

<label> y una etiqueta de formulario (<input> o <textarea>).

1.2.3. Labels

Los títulos o labels de cada campo se generan de forma automática. La lógica que se utiliza por defecto para convertir los nombres de los campos en sus títulos sigue dos reglas:

y  La primera letra se convierte en mayúscula

y  Los guiones bajos se convierten en espacios en blanco

A continuación se muestra otro ejemplo:

$this->setWidgets(array('codigo_postal' =>new sfWidgetFormInput(), // Título generado:"Codigo postal"'fecha_nacimiento' =>new sfWidgetFormInput(), // Título generado: "Fecha

nacimiento"));

Aunque la generación automática de los títulos es muy útil, el framework también permite

definir títulos personalizados con el método setLabels():

$this->widgetSchema->setLabels(array('nombre' =>'Tu nombre','email' =>'Tu correo electrónico','mensaje' =>'Tu mensaje',));

También se puede modificar un solo título mediante el método setLabel():

$this->widgetSchema->setLabel('email', 'Tu correo electrónico');

El capítulo 3 muestra cómo se pueden personalizar los títulos desde la plantilla para

 personalizar aun más los formularios.

Widget Schema

Cuando se utiliza el método setWidgets(), Symfony crea un objeto de tipo

sfWidgetFormSchema. Este objeto es un widget que representa a un conjunto de widgets.

En nuestro formulario ContactoForm hemos utilizado el método setWidgets(), que esequivalente al siguiente código:

$this->setWidgetSchema(new sfWidgetFormSchema(array('nombre' =>new sfWidgetFormInput(),'email' =>new sfWidgetFormInput(),'mensaje' =>new sfWidgetFormTextarea(),)));

// es equivalente a:

Page 7: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 7/167

 

 $this->widgetSchema = new sfWidgetFormSchema(array('nombre' =>new sfWidgetFormInput(),'email' =>new sfWidgetFormInput(),'mensaje' =>new sfWidgetFormTextarea(),));

El método setLabels() se aplica al conjunto de widgets incluídos en el objecto

widgetSchema.

En el capítulo 5 se explica por qué el concepto de "  schema widget " hace más fácil el

manejo de formularios complejos.

1.2.4. Más allá de las tablas generadas

Aunque por defecto el formulario se muestra con una tabla HTML, su diseño se puedemodificar completamente. Los diferentes tipos de diseños o layouts se definen en clases que

heredan de sfWidgetFormSchemaFormatter. El estilo por defecto de los formularioscorresponde a la clase sfWidgetFormSchemaFormatterTable. También es posible utilizar 

el diseño list, basado en una lista HTML:

class ContactoForm extends sfForm{publicfunction configure(){$this->setWidgets(array('nombre' =>new sfWidgetFormInput(),'email' =>new sfWidgetFormInput(),'mensaje' =>new sfWidgetFormTextarea(),));

$this->widgetSchema->setFormFormatterName('list');}}

Los diseños de tabla y de lista están incluídos por defecto en Symfony. En el capítulo 5 se

explica cómo crear tus propias clases de formato para personalizar el diseño de los

formularios. Después de mostrar cómo se visualiza un formulario, el siguiente paso consisteen realizar el envío de los datos introducidos por el usuario.

1.2.5. Enviando el formulario

Al crear la plantilla que muestra el formulario, se empleó la URL interna

contacto/enviar en la etiqueta <form> para enviar el formulario. Por tanto, el siguiente

 paso consiste en añadir la acción enviar en el módulo contacto. El listado 1-5 muestracómo obtener en la acción la información enviada por el usuario y cómo se le puede

redirigir a la página de agradecimiento, donde se vuelve a mostrar la misma información.

Listado 1-5 - La acción enviar en el módulo contacto 

Page 8: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 8/167

 

publicfunction executeEnviar($request){$this->forward404Unless($request->isMethod('post'));

$parametros = array('nombre' =>$request->getParameter('nombre'),'email' =>$request->getParameter('email'),'mensaje' =>$request->getParameter('mensaje'),);

$this->redirect('contacto/gracias?'.http_build_query($parametros));}

publicfunction executeGracias(){}// apps/frontend/modules/contacto/templates/graciasSuccess.php<ul><li>Nombre: <?phpecho$sf_params->get('nombre') ?></li><li>Email: <?phpecho$sf_params->get('email') ?></li>

<li>Mensaje: <?phpecho$sf_params->get('mensaje') ?></li></ul>

Nota 

http_build_query es una función propia de PHP que genera una cadena de texto de tipoquery string a partir de los parámetros pasados a través de un array y con sus valores

correctamente codificados para incluirlos en una URL.

El método executeEnviar() realiza tres acciones:

y  Por razones de seguridad, se comprueba que la página se ha enviado utilizando el método

POST. Si no es así, se redirige al usuario a una página de error 404. En la plantillaindexSuccess, se había declarado que el método de envío del formulario debe serPOST 

(<form ... method="POST">): 

$this->forward404Unless($request->isMethod('post'));

y  A continuación se obtienen los valores introducidos por el usuario y se guardan en un

array llamado parametros: 

$parametros = array('nombre' =>$request->getParameter('nombre'),'email' =>$request->getParameter('email'),

'mensaje' =>$request->getParameter('mensaje'),);

y  Por último, se redirige al usuario a la página de agradecimiento (contacto/gracias)

para mostrarle la información que ha introducido: 

$this->redirect('contacto/gracias?'.http_build_query($parametros));

Page 9: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 9/167

 

En vez de redirigir al usuario a otra página, se podría haber creado una plantilla llamada

enviarSuccess.php. No obstante, aunque es posible hacerlo, después de un envío por el

método POST se recomienda redirigir al usuario a otra página, ya que:

y  Se evita que el usuario envíe de nuevo el formulario si recarga la página de

agradecimiento.y  El usuario puede volver a la página anterior sin que se le muestre el mensaje de enviar el

formulario de nuevo.

Sugerencia 

Quizás has observado que el método executeEnviar() es diferente de executeIndex().Cuando se invocan estos dos métodos, Symfony les pasa automáticamente como primer 

argumento el objeto de tipo sfRequest que representa a la petición actual. Como en PHPno es necesario recoger todos los parámetros que se pasan a una función, el método

executeIndex() no incluye el parámetro $request en su definición, ya que no lo utiliza

 para nada.

La figura 1-5 muestra el flujo de trabajo de todos los métodos que intervienen en lainteracción del usuario.

Figura 1.5. Flujo de trabajo de los métodos

Nota 

Cuando se vuelve a mostrar la información del usuario en la plantilla, se corre el riesgo desufrir un ataque de tipo XSS (C ross-Site Scripting ). Puedes consultar el capítulo sobre la

 parte de la vista del libro oficial de Symfony para conocer más detalles sobre cómo evitar los ataques XSS.

Después de enviar el formulario, deberías ver la página de la figura 1-6.

Page 10: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 10/167

 

 

Figura 1.6. Página que se muestra después de enviar el formulario

Por otra parte, en vez de crear el array parametros, sería más sencillo recoger la

información del usuario directamente en un array. El listado 1-6 modifica el atributo name 

de HTML de los widgets para guardar los valores en un array llamado contacto.

Listado 1-6 - Modificando el atributo nam e de HTML de los widgets 

class ContactoForm extends sfForm{publicfunction configure(){

$this->setWidgets(array('nombre' =>new sfWidgetFormInput(),'email' =>new sfWidgetFormInput(),'mensaje' =>new sfWidgetFormTextarea(),));

$this->widgetSchema->setNameFormat('contacto[%s]');}}

El método setNameFormat() permite modificar el atributo name en todos los widgets.

Cuando se genera el formulario, el valor %s se reemplaza automáticamente por el nombre

de cada campo. Si se toma como ejemplo el campo email, su atributo name de HTML será

contacto[email]. Si se utilizan estos nombres, PHP crea automáticamente un array queincluye todos los valores enviados en la petición. De esta forma, los valores de los campos

se pueden acceder mediante el array contacto.

Ahora en la acción se puede obtener el array contacto directamente a partir del objeto dela petición, tal y como se muestra en el listado 1-7.

Listado 1-7 - Nuevo formato de los atributos nam e de los widgets 

publicfunction executeEnviar($request){

$this->forward404Unless($request->isMethod('post'));

$this->redirect('contacto/gracias?'.http_build_query($request->getParameter('contacto')));}

Si observas el código HTML del formulario generado, verás que Symfony no sólo crea un

atributo name a partir del nombre y formato de cada widget, sino que también crea un

Page 11: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 11/167

 

atributo id. En realidad, el atributo id es una copia del atributo name en la que se han

sustituido los caracteres problemáticos por un guión bajo (_):

Nombre del widget  Atributo nam e  Atributo id  

nombre  contacto[nombre]  contacto_nombre

email  contacto[email]  contacto_email

mensaje  contacto[mensaje] contacto_mensaje

1.2.6. Una solución alternativa

En el ejemplo anterior se utilizan dos acciones para gestionar el formulario: acción index 

 para mostrarlo y acción enviar para enviarlo. Como el formulario se visualiza con el

método GET y se envía con el método POST, se pueden fusionar los dos métodos en unaúnica acción index como se muestra en el listado 1-8.

Listado 1-8 - Fusionando las dos acciones utilizadas en el formulario 

class contactoActions extends sfActions{publicfunction executeIndex($request){$this->formulario = new ContactoForm();

if($request->isMethod('post')){$this->redirect('contacto/gracias?'.http_build_query($request->getParameter('contacto')));}}}

Para que el código anterior funcione correctamente, no olvides modificar el atributo action 

del formulario en la plantilla indexSuccess.php:

<formaction="<?php echo url_for('contacto/index') ?>" method="POST">

Como se verá más adelante, es mejor utilizar esta sintaxis porque es más breve y hace que

el código resultante sea más coherente y fácil de entender.

Page 12: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 12/167

 

1.3. Configurando los widgets

1.3.1. Opciones de los widgets

Si el sitio web que muestra el formulario de contacto dispone de varios responsables, puede

ser interesante añadir una lista desplegable con diferentes temas de contacto para redirigir cada mensaje al responsable adecuado (ver figura 1-7). El listado 1-9 añade un nuevo

campo llamado asunto mediante una lista desplegable creada con un widget de tipo

sfWidgetFormSelect.

Figura 1.7. Añadiendo un campo asunto en el formulario

Listado 1-9 - Añadiendo un campo asunto en el formulario 

class ContactoForm extends sfForm{protected static$asuntos = array('Asunto A', 'Asunto B', 'Asunto C');

publicfunction configure(){$this->setWidgets(array('nombre' =>new sfWidgetFormInput(),'email' =>new sfWidgetFormInput(),'asunto' =>new sfWidgetFormSelect(array('choices' => self::$asuntos)),

'mensaje' =>new sfWidgetFormTextarea(),));

$this->widgetSchema->setNameFormat('contacto[%s]');}}

La opción ''choices'' del widget ''sfWidgetFormSelect''

Page 13: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 13/167

 

PHP no considera diferentes los arrays normales y los arrays asociativos, por lo que el array

utilizado en la opción asunto del código anterior es idéntico al siguiente:

$asuntos = array(0 =>'Asunto A', 1 =>'Asunto B', 2 =>'Asunto C');

El widget generado toma la clave del array como el atributo value de la etiqueta <option> 

y el valor del array como el contenido de la etiqueta:

<selectname="contacto[asunto]"id="contacto_asunto"><optionvalue="0">Asunto A</option><optionvalue="1">Asunto B</option><optionvalue="2">Asunto C</option></select>

Si quieres modificar el valor de los atributos value, debes añadir claves en el arrayutilizado:

$asuntos = array('A' =>'Asunto A', 'B' =>'Asunto B', 'C' =>'Asunto C');

Si se utiliza el array anterior, el código HTML generado es el siguiente:

<selectname="contacto[asunto]"id="contacto_asunto"><optionvalue="A">Asunto A</option><optionvalue="B">Asunto B</option><optionvalue="C">Asunto C</option></select>

El widget sfWidgetFormSelect, como todos los demás widgets, acepta como primer argumento una lista de opciones. A pesar de su nombre, las opciones pueden ser opcionales

u obligatorios. El widget sfWidgetFormSelect dispone de una opción obligatoria llamadachoices. A continuación se muestran las opciones disponibles para los widgets que yahemos utilizado:

Widget Opciones obligatorias Otras opciones

sfWidgetFormInput  -

type: tipo de campo (por defecto, text)

is_hidden: indica si el campo es oculto

(por defecto, false)

sfWidgetFormSelect 

choices: opciones de la

lista desplegable

multiple: la lista permite selecciones

múltiples (por defecto, false)

sfWidgetFormTextarea - -

Sugerencia 

Si quieres conocer todas las opciones de un widget, puedes consultar la documentación dela API disponible online en http://www.symfony-project.org/api/1_2/. En la API se

Page 14: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 14/167

 

explican todas las opciones y todos sus valores por defecto. Para consultar por ejemplo las

opciones del widget sfWidgetFormSelect debes acceder a http://www.symfony- project.org/api/1_2/sfWidgetFormSelect 

1.3.2. Los atributos HTML de los widgets

Los widgets también aceptan como segundo argumento una lista de atributos HTML. Deesta forma, esta opción permite personalizar las etiquetas HTML generadas para el

formulario. El listado 1-10 muestra como añadir un atributo class al campo email.

Listado 1-10 - Definiendo atributos HTML para un widget 

$widgetEmail = new sfWidgetFormInput(array(), array('class' =>'email'));

// Código HTML generado<input type="text" name="contacto[email]"class="email"id="contacto_email" />

Los atributos HTML también permiten redefinir los identificadores generados

automáticamente, como se muestra en el listado 1-11.

Listado 1-11 - Redefiniendo el atributo id  

$widgetEmail = new sfWidgetFormInput(array(), array('class' =>'email','id' =>'email'));

// Código HTML generado<input type="text" name="contacto[email]"class="email" id="email" />

También es posible establecer el valor que muestran por defecto los campos utilizando elatributo value como se muestra en el listado 1-12.

Listado 1-12 - Establecer valores por defecto de los widgets utilizando atributos

HTML 

$widgetEmail = new sfWidgetFormInput(array(), array('value' =>'Escribe tuemail'));

// Código HTML generado<input type="text" name="contacto[email]" value="Escribe tu email"id="contacto_email" />

Esta opción funciona bien para los widgets de tipo input, pero es difícil de aplicar en los

widgets de tipo checkbox o radio e incluso es imposible para uno de tipo textarea. La

clase sfForm incluye métodos específicos para definir los valores por defecto de cadacampo de una forma uniforme para cualquier tipo de widget.

Nota 

Page 15: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 15/167

 

Una buena práctica consiste en definir los atributos de HTML dentro de la plantilla y no enel formulario (donde también es posible hacerlo), para mantener la separación de las capastal y como se verá en el capítulo 3.

1.3.3. Definir los valores por defecto de los campos

En ocasiones es conveniente definir un valor por defecto para cada campo. El ejemplo

típico es el mensaje de ayuda que se muestra en cada campo del formulario y que se ocultacuando el usuario se situa en ese campo. El listado 1-13 muestra cómo definir los valores

 por defecto a través de los métodos setDefault() y setDefaults().

Listado 1-13 - Establecer los valores por defecto en los widgets mediante los métodos

setDefault() y setDefaults() 

class ContactoForm extends sfForm{publicfunction configure()

{// ...

$this->setDefault('email', 'Escribe tu email');

$this->setDefaults(array('email' =>'Escribe tu email', 'nombre'=>'Escribe tu nombre'));}}

Los métodos setDefault() y setDefaults() son muy útiles para definir los mismosvalores por defecto para cada instancia del mismo formulario. Si se quiere modificar un

objeto existente utilizando un formulario, los valores por defecto dependen de la instancia y por tanto, deben ser dinámicos. El listado 1-14 muestra cómo el constructor de la clase

sfForm dispone de un primer argumento que establece dinámicamente los valores por defecto.

Listado 1-14 - Establecer los valores por defecto de los widgets mediante el

constructor de sfForm  

publicfunction executeIndex($peticion){$this->formulario = new ContactoForm(array('email' =>'Escribe tu email','nombre' =>'Escribe tu nombre'));

// ...}

Protección frente a XSS (Cross-Site Scripting)

Cuando se establecen atributos HTML para los widgets o cuando se definen valores por 

defecto, la clase sfForm protege automáticamente estos valores contra ataques XSS al

generar el código HTML. Esta protección no depende de la opción escaping_strategy 

Page 16: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 16/167

 

configurada en el archivo settings.yml. Si el contenido ya ha sido protegido por otrométodo, no se le aplica ninguna otra protección.

La clase sfForm también protege los caracteres ' y ", ya que pueden provocar que elcódigo HTML generado no sea válido.

A continuación se muestra un ejemplo de esta protección:

$widgetEmail = new sfWidgetFormInput(array(), array('value' =>'Hola "Mundo"!','class' =>'<script>alert("hola")</script>',));

// Código HTML generado<inputvalue="Hola &quot;Mundo!&quot;"

class="&lt;script&gt;alert(&quot;hola&quot;)&lt;/script&gt;"type="text" name="contacto[email]" id="contacto_email"

/>

Capítulo 2. Validación de formularios

En el capítulo 1, se mostró cómo crear y visualizar un formulario de contacto. En este

capítulo se explica cómo gestionar la validación del formulario.

2.1. Antes de comenzar

El formulario de contacto creado en el capítulo 1 todavía no es muy útil. ¿Qué sucede si un

usuario envía una dirección de email inválida o si el mensaje enviado está vacío? En estoscasos, lo habitual es mostrar mensajes de error que le indiquen al usuario que tiene que

corregir los datos enviados, como se muestra en la figura 2-1.

Page 17: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 17/167

 

Figura 2.1. Mostrando mensajes de error 

A continuación se describen las reglas de validación que se van a incluir en el formulario de

contacto:

y nombre: opcional

y  email: obligatorio, debe ser una dirección válida de correo electrónico

y  asunto: obligatorio, seleccionado entre una lista de valores predefinidos

y  mensaje: obligatorio, debe tener una longitud de al menos cuatro caracteres

Nota 

¿Por qué es necesario validar el campo asunto? Al fin y al cabo, su valor se selecciona

mediante una etiqueta <select> que obliga al usuario a seleccionar un valor entre una lista

de valores predefinidos. Aunque es cierto que los usuarios normales sólo podránseleccionar uno de los valores predefinidos, cualquier usuario con conocimientos medios

 puede utilizar herramientas como Firebug para manipular los valores de la listadesplegable. Además, otros usuarios pueden utilizar herramientas como curl o wget paraconstruir peticiones HTTP a medida que incluyan valores arbitrarios.

El listado 2-1 muestra la misma plantilla que se utiliza en el capítulo 1.

Listado 2-1 - La plantilla del formulario Contacto 

// apps/frontend/modules/contacto/templates/indexSucces.php<form action="<?php echo url_for('contacto/index') ?>" method="POST"><table><?php echo $formulario ?>

<tr><td colspan="2"><input type="submit" /></td></tr></table></form>

La figura 2-2 muestra la interacción entre la aplicación y el usuario. El primer paso consiste

en mostrar la información al usuario. Cuando el usuario envía el formulario, si los datos sonválidos se le redirige a la página de agradecimiento y si los datos no cumplen alguna de las

reglas de validación, se vuelve a mostrar el formulario con los mensajes de error.

Page 18: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 18/167

 

Figura 2.2. Interacción entre la aplicación y el usuario

2.2. ValidadoresLos formularios de Symfony están formados por campos. En el capítulo 1, se explica cómose identifican los campos mediante un nombre único. Para mostrar el formulario al usuario,

se asocia un tipo de widget a cada campo. De la misma forma, ahora se van a aplicar lasreglas de validación a cada campo.

Page 19: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 19/167

 

2.2.1. La clase sfValidatorBase

La validación de los campos se realiza mediante objetos que heredan de la clase

sfValidatorBase. Para validar el formulario de contacto, se define un objeto validador 

 para cada uno de los cuatro campos: nombre, email, asunto, y mensaje. El listado 2-2

muestra cómo crear estos validadores en la clase del formulario utilizando el métodosetValidators().

Listado 2-2 - Añadiendo validadores en la clase ContactoForm  

// lib/form/ContactoForm.class.phpclass ContactoForm extends sfForm{protected static$asuntos = array('Asunto A', 'Asunto B', 'Asunto C');

publicfunction configure(){$this->setWidgets(array('nombre' =>new sfWidgetFormInput(),'email' =>new sfWidgetFormInput(),'asunto' =>new sfWidgetFormSelect(array('choices' => self::$asuntos)),'mensaje' =>new sfWidgetFormTextarea(),));$this->widgetSchema->setNameFormat('contacto[%s]');

$this->setValidators(array('nombre' =>new sfValidatorString(array('required' =>false)),'email' =>new sfValidatorEmail(),'asunto' =>new sfValidatorChoice(array('choices'=>array_keys(self::$asuntos))),'mensaje' =>new sfValidatorString(array('min_length' =>4)),

));}}

El código anterior utiliza tres validadores diferentes:

y  sfValidatorString: valida una cadena de texto

y  sfValidatorEmail : valida un email

y  sfValidatorChoice: valida que el valor se encuentra entre una lista de valores

predefinidos

Los validadores aceptan como primer argumento una lista de opciones. Al igual que los

widgets, a pesar de su nombre algunas opciones son opcionales y otras obligatorias. El

validador sfValidatorChoice por ejemplo dispone de una opción obligatoria llamada

choices. Todos los validadores pueden establecer las opciones required y trim, que

definen sus valores por defecto en la clase sfValidatorBase:

OpciónValor por

Descripción

Page 20: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 20/167

 

defecto 

required true  Indica que es obligatorio rellenar este campo

trim  false Indica si se deben eliminar los espacios en blanco del principio y del final

antes de validar el valor del campo

A continuación se muestran las opciones de los validadores del ejemplo anterior:

Validador Opciones obligatorias Otras opciones

sfValidatorString - max_length, min_length 

sfValidatorEmail  - pattern 

sfValidatorChoice choices  -

Si se envía ahora el formulario con valores incorrectos, no se produce ningún cambio

respecto al comportamiento sin la validación. El motivo es que se debe modificar el módulo

contacto para que aplique las reglas de validación, tal y como se muestra en el listado 2-3.

Listado 2-3 - Añadiendo la validación en el módulo contacto 

class contactoActions extends sfActions{

publicfunction executeIndex($request){$this->formulario = new ContactoForm();

if($request->isMethod('post')){$this->formulario->bind($request->getParameter('contacto'));if($this->formulario->isValid()){$this->redirect('contacto/gracias?'.http_build_query($this->formulario->getValues()));}}}

publicfunction executeGracias(){}}

El listado 2-3 incluye numerosos conceptos nuevos:

Page 21: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 21/167

 

y  Cuando se realiza la petición GET inicial, se inicializa el formulario y se pasa a la plantilla

para mostrarlo al usuario. En este caso, el formulario se encuentra en el estado inicial: 

$this->formulario = new ContactoForm();

y  Cuando el usuario envía el formulario mediante una peticiónPOST, el método bind() 

asocia el formulario con los datos introducidos por el usuario y ejecuta el mecanismo de

validación. En este caso, el formulario se encuentra en el estado asociado.

if($request->isMethod('post')){$this->formulario->bind($request->getParameter('contacto'));

y  Una vez asociado, el formulario se puede validar mediante el métodoisValid(): 

o  Si el valor devuelto por el método es true, el formulario es válido y se redirige al

usuario a la página de agradecimiento: 

if($this->formulario->isValid()){$this->redirect('contacto/gracias?'.http_build_query($this->formulario->getValues()));}

y  En caso contrario, se vuelve a mostrar la plantilla indexSuccess como al principio. El

proceso de validación añade los mensajes de error al formulario para que se muestren al

usuario.

Nota 

Cuando el formulario se encuentra en el estado inicial, el método isValid() siempre

devuelve false y el método getValues() siempre devuelve un array vacío.

La figura 2-3 muestra el código que se ejecuta durante la interacción entre la aplicación y el

usuario.

Page 22: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 22/167

 

Figura 2.3. Código que se ejecuta durante la interacción entre la aplicación y el usuario

2.2.2. La finalidad de los validadores

En el código anterior, durante la redirección a la página de agradecimiento, no se utiliza la

instrucción $request->getParameter('contacto') sino que se emplea $this-

>formulario->getValues(). De hecho, la instrucción $request-

>getParameter('contacto') devuelve los datos enviados por el usuario y la instrucción

$this->formulario->getValues() devuelve los datos validados.

Y si el formulario es válido, ¿por qué las dos instrucciones anteriores no son equivalentes?

En realidad, los validadores realizan dos tareas: la tarea de validación y la tarea de

limpieza. Por tanto, el método getValues() devuelve los datos validados y limpios.

Page 23: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 23/167

 

A su vez, el proceso de limpieza de datos se compone de dos acciones: normalización y

conversión de datos.

La opción trim explicada anteriormente es un caso de normalización de datos. No obstante,la normalización es un proceso mucho más importante por ejemplo para las fechas. El

validador sfValidatorDate se encarga de validar fechas y permite introducir la fecha conmuchos formatos (un timestamp, un formato que sigue una expresión regular, etc.). Este

validador no devuelve directamente el valor introducido, sino que por defecto lo convierte

al formato Y-m-d H:i:s. Por lo tanto, el programador puede estar seguro de obtener 

siempre el mismo formato para las fechas, independientemente del formato en el que elusuario introdujo la fecha. Este mecanismo ofrece una gran flexibilidad a los usuarios y

asegura la consistencia a los programadores.

Respecto a la conversión de datos, se muestra a continuación el caso de un fichero subido.

La validación de los archivos subidos por los usuarios se realiza con el validador 

sfValidatorFile. Una vez que el archivo se ha subido, este validador no devuelve

simplemente el nombre del archivo, sino que devuelve un objeto de tiposfValidatedFile, que facilita el acceso a la información del archivo. Más adelante en estecapítulo se muestra cómo utilizar este validador.

Sugerencia 

El método getValues() devuelve un array con todos los datos validados y limpios. Sin

embargo, como en ocasiones es muy útil acceder sólo a un valor, también existe el método

getValue(). Ejemplo de uso: $email = $this->formulario->getValue('email').

2.2.3. Formularios inválidos

Cuando alguno de los campos del formulario contiene información inválida, se muestra la

 plantilla indexSuccess. La figura 2-4 muestra el resultado obtenido cuando se envíandatos inválidos.

Page 24: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 24/167

 

 

Figura 2.4. Formulario inválido

La instrucción <?php echo $formulario ?> tiene en cuenta los mensajes de error asociados a cada campo y vuelve a mostrar los datos introducidos por el usuario después de pasar por el proceso de limpieza de datos.

Cuando se asocia el formulario con datos externos mediante el método bind(), elformulario pasa al estado asociado y se ejecutan las siguientes acciones:

y  Se ejecuta el proceso de validación

y  Los mensajes de error se almacenan en el formulario para que estén disponibles en la

plantilla

y  Los valores iniciales del formulario se reemplazan por los valores resultantes del proceso

de limpieza de datos

La información necesaria para mostrar los mensajes de error o los datos introducidos por el

usuario se puede acceder fácilmente mediante la variable formulario en la plantilla.

Cuidado 

Como se explica en el capítulo 1, el constructor de la clase del formulario admite que se le

 pasen los valores por defecto. Después de enviar un formulario con datos inválidos, estosvalores por defecto se sustituyen por los datos enviados, de forma que el usuario pueda ver 

los errores que ha cometido. Por lo tanto, no se deben utilizar los datos introducidos como

si fueran datos por defecto, como por ejemplo en la siguiente instrucción: $this->formulario->setDefaults($peticion->getParameter('contacto'))  

2.3. Personalizando los validadores

Page 25: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 25/167

 

2.3.1. Personalizar los mensajes de error

Como se aprecia en la figura 2-4 anterior, los mensajes de error por defecto no son muy

útiles. A continuación se muestra cómo personalizarlos para que sean más intuitivos.

Todos los validadores pueden añadir errores en el formulario. Un error está formado por uncódigo de error y un mensaje de error. Cada validador dispone al menos de los errores

required y invalid, definidos en la clase sfValidatorBase:

Código Mensaje Descripción

required Required. Es obligatorio rellenar el campo y ahora está vacío

invalid  Invalid.  La información del campo no es válida

A continuación se muestran los códigos de error de los validadores que se han visto hastaahora:

Validador Código de error

sfValidatorString max_length, min_length 

sfValidatorEmail  -

sfValidatorChoice -

Para personalizar los mensajes de error, se pasa un segundo argumento a los objetos

validadores. El listado 2-4 personaliza varios mensajes de error y la figura 2-5 muestra elresultado.

Listado 2-4 - Personalizando mensajes de error 

class ContactoForm extends sfForm{protected static$asuntos = array('Asunto A', 'Asunto B', 'Asunto C');

publicfunction configure(){// ...

$this->setValidators(array('nombre' =>new sfValidatorString(array('required' =>false)),'email' =>new sfValidatorEmail(array(), array('invalid' =>'La direcciónde email no es válida.')),'asunto' =>new sfValidatorChoice(array('choices'=>array_keys(self::$asuntos))),

Page 26: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 26/167

 

'mensaje' =>new sfValidatorString(array('min_length' =>4),array('required' =>'Es obligatorio escribir un mensaje.')),));}}

Figura 2.5. Mensajes de error propios

La figura 2-6 muestra el mensaje de error que se obtiene si se envía un mensaje muy corto,ya que se ha establecido que su longitud mínima debe ser cuatro caracteres:

Figura 2.6. Error producido porque el mensaje es muy corto

Page 27: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 27/167

 

El mensaje de error por defecto asociado con el código de error min_length es diferente delos otros mensajes de error, ya que dispone de dos variables: los datos introducidos por el

usuario (foo en el ejemplo anterior) y el número mínimo de caracteres de este campo (4 enel ejemplo anterior). El listado 2-5 personaliza aún más el mensaje de error utilizando estas

dos vairables y la figura 2-7 muestra el resultado.

Listado 2-5 - Personalizando los mensajes de error con variables 

class ContactoForm extends sfForm{publicfunction configure(){// ...

$this->setValidators(array('nombre' =>new sfValidatorString(array('required' =>false)),'email' =>new sfValidatorEmail(array(), array('invalid' =>'La direcciónde email no es válida.')),

'asunto' =>new sfValidatorChoice(array('choices'=>array_keys(self::$asuntos))),'mensaje' =>new sfValidatorString(array('min_length' =>4), array('required' =>'Es obligatorio escribir un mensaje.','min_length' =>'El mensaje "%value%" es demasiado corto. Su longitud debeser al menos de %min_length% caracteres.',)),));}}

Figura 2.7. Mensajes de error personalizados con variables

Todos los mensajes de error pueden utilizar variables simplemente encerrando su nombre

con el carácter del porcentaje (%). Las variables disponibles normalmente son los datos

Page 28: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 28/167

 

introducidos por el usuario (que se llama value) y los valores definidos por cada validador 

(min_length, max_length, etc.)

Sugerencia 

Si quieres conocer todos los códigos de error, opciones y mensajes por defecto de losvalidadores, puedes consultar la documentación online de la API en http://www.symfony-

 project.org/api/1_2/ Por ejemplo, las opciones, códigos y mensajes de error del validador 

sfValidatorString se pueden consultar en http://www.symfony- project.org/api/1_2/sf ValidatorString 

2.4. Seguridad de los validadores

Por defecto, los formularios sólo son válidos si todos los campos rellenados por el usuariodisponen de un validador. De esta forma, Symfony se asegura que todos los campos tienen

establecidas reglas de validación y también se asegura que no sea posible incluir información en campos que no están definidos en el formulario original.

Para comprender mejor la seguridad de los formularios, se muestra el ejemplo del listado 2-6 en el que se utiliza un objeto de tipo usuario.

Listado 2-6 - La clase Usuario 

class Usuario{protected

$nombre = '',$es_administrador = false;

publicfunction setCampos($campos){if(isset($campos['nombre'])){$this->nombre = $campos['nombre'];}

if(isset($campos['es_administrador'])){$this->es_administrador = $campos['es_administrador'];}}

// ...}

El objeto Usuario incluye dos propiedades, el nombre del usuario (nombre) y un valor 

booleano que indica si el usuario es de tipo administrador (es_administrador). El método

setCampos() actualiza el valor de estas dos propiedades. El listado 2-7 muestra el

Page 29: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 29/167

 

formulario relacionado con la clase Usuario y que permite modificar solamente la

 propiedad nombre.

Listado 2-7 - Formulario Usuario 

class UsuarioForm extends sfForm{publicfunction configure(){$this->setWidgets(array('nombre' =>new sfWidgetFormInputString()));$this->widgetSchema->setNameFormat('usuario[%s]');

$this->setValidators(array('nombre' =>new sfValidatorString()));}}

El listado 2-8 muestra el código de un módulo llamado usuario que utiliza el formulario

anterior para permitir al usuario cambiar el valor del campo nombre.

Listado 2-8 - Código del módulo usuario 

class usuarioActions extends sfActions{publicfunction executeIndex($request){$this->formulario = new UsuarioForm();

if($request->isMethod('post')){$this->formulario->bind($request->getParameter('usuario'));if($this->formulario->isValid())

{$usuario = // obtener el objeto del usuario

$usuario->setCampos($this->formulario->getValues());

$this->redirect('...');}}}}

Si no se utiliza ninguna protección, el código de la aplicación es vulnerable ya que el

usuario podría enviar el formulario con los campos nombre y es_administrador rellenos.Modificar el código HTML de los formularios para enviar cualquier información es muy

sencillo gracias a herramientas como Firebug. Además, como el campo es_administrador no tiene asociado ningún validador, su valor siempre es válido. Por lo tanto, sea cual sea su

valor, el método setCampos() no sólo actualiza la propiedad nombre sino que también

modifica la propiedad es_administrador.

Si se prueba el código anterior con un formulario que incluya los campos nombre y

es_administrador, se produce un error global y se muestra el mensaje de error "Extra

Page 30: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 30/167

 

 f  ield es_administrador , tal y como se observa en la figura 2-8. El error se produce porquealgunos de los campos enviados no tienen ningún validador asociado, ya que el campo

es_administrador no está definido en el formulario UsuarioForm.

Figura 2.8. Error producido porque no se ha definido un validador 

Todos los validadores utilizados hasta el momento generan errores asociados con sus propios campos. Por lo tanto, ¿de dónde proviene este error global? Cuando se emplea el

método setValidators(), Symfony crea un objeto de tipo sfValidatorSchema que a su

vez define una colección de validadores. Invocar el método setValidators() esequivalente al siguiente código:

$this->setValidatorSchema(new sfValidatorSchema(array('email' =>new sfValidatorEmail(),'asunto' =>new sfValidatorChoice(array('choices'=>array_keys(self::$asuntos))),'mensaje' =>new sfValidatorString(array('min_length' =>4)),)));

El objeto sfValidatorSchema incluye dos reglas de validación activadas por defecto para

 proteger la colección de validadores. Estas reglas se pueden configurar con las opciones

allow_extra_fields y filter_extra_fields.

La opción allow_extra_fields, que vale false por defecto, comprueba si todos loscampos enviados disponen de un validador. En caso negativo, genera el error global "Extra f  ield <nombre_del_campo>." , como sucedía en el ejemplo anterior. Cuando se desarrolla la

aplicación esta opción es muy útil porque avisa a los programadores sobre todos los camposque todavía no disponen de un validador.

Volviendo al formulario de contacto, vamos a modificar sus reglas de validación y vamos a

establecer que el campo nombre es obligatorio. Como el valor por defecto de la opción

required es true, se puede establecer el validador de nombre simplemente como:

$validadorNombre = new sfValidatorString();

Como el validador no establece ni la opción min_length ni max_length, no tiene efectosobre el texto introducido. De hecho, se podría reemplazar por un validador vacío:

$validadorNombre = new sfValidatorPass();

Page 31: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 31/167

 

En el ejemplo anterior, en vez de utilizar un validador vacío, se puede eliminar por 

completo el validador, pero la protección por defecto del formulario impide eliminar elvalidador. El listado 2-9 muestra cómo deshabilitar la protección por defecto mediante la

opción allow_extra_fields.

Listado 2-9 - Deshabilitando la protección allow_ 

extra_ 

fields 

class ContactoForm extends sfForm{publicfunction configure(){// ...

$this->setValidators(array('email' =>new sfValidatorEmail(),'asunto' =>new sfValidatorChoice(array('choices'=>array_keys(self::$asuntos))),'mensaje' =>new sfValidatorString(array('min_length' =>4)),));

$this->validatorSchema->setOption('allow_extra_fields', true);}}

Con el código anterior, ya es posible validar el formulario como se muestra en la figura 2-9.

Figura 2.9. Validando el formulario con la opción allow_extra_fields activada

Si observas con atención la imagen anterior, verás que aunque el formulario es válido, el

valor del campo nombre está vacío en la página de agradecimiento, independientemente del

valor que se haya escrito el usuario en ese campo. De hecho, el valor enviado no existe ni

siquiera en el array que se obtiene mediante $this->formulario->getValues().

Deshabilitar la opción allow_extra_fields evita que se muestre el error debido a la falta

del validador, pero como la otra opción llamada filter_extra_fields vale true por 

defecto, se filtran todos los campos que no tienen validador, por lo que sus valores no seencuentran entre los valores válidos. El listado 2-10 muestra cómo modificar este

comportamiento.

Listado 2-10 - Deshabilitando la protección de filter_ extra_ fields 

class ContactoForm extends sfForm{publicfunction configure(){// ...

Page 32: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 32/167

 

 $this->setValidators(array('email' =>new sfValidatorEmail(),'asunto' =>new sfValidatorChoice(array('choices'=>array_keys(self::$asuntos))),'mensaje' =>new sfValidatorString(array('min_length' =>4)),));

$this->validatorSchema->setOption('allow_extra_fields', true);$this->validatorSchema->setOption('filter_extra_fields', false);}}

Ahora ya es posible validar el formulario y obtener el valor introducido por el usuario paramostrarlo en la página de agradecimiento.

En el capítulo 4 se muestra cómo utilizar estas protecciones para serializar de forma segura

los objetos Propel a partir de los valores del formulario.

2.5. Validadores lógicos

Un único campo puede definir varios validadores utilizando los validadores lógicos:

y  sfValidatorAnd: para ser válido, el campo debe pasar todos los validadores

y  sfValidatorOr: para ser válido, el campo debe pasar al menos un validador 

Los constructores de los validadores lógicos aceptan como primer argumento una lista de

validadores. El listado 2-11 emplea sfValidatorAnd para establecer dos validadores

obligatorios en el campo nombre.

Listado 2-11 - Empleando el validador sfV alidatorAnd  

class ContactoForm extends sfForm{public function configure(){

// ...

$this->setValidators(array(// ...'nombre' => new sfValidatorAnd(array(new sfValidatorString(array('min_length' => 5)),

new sfValidatorRegex(array('pattern' => '/[\w- ]+/')),)),

));}

}

Cuando se envía el formulario, el valor del campo nombre debe tener al menos cinco

caracteres y debe cumplir con la expresión regular [\w- ]+ 

Page 33: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 33/167

 

Como los validadores lógicos también son validadores, se pueden combinar para crear 

validaciones tan complejas como las mostradas en el listado 2-12.

Listado 2-12 - Combinando varios validadores lógicos 

class ContactoForm extends sfForm{public function configure(){

// ...

$this->setValidators(array(// ...'nombre' => new sfValidatorOr(array(new sfValidatorAnd(array(new sfValidatorString(array('min_length' => 5)),new sfValidatorRegex(array('pattern' => '/[\w- ]+/')),

)),new sfValidatorEmail(),

)),));

}}

2.6. Validadores globales

Los validadores mostrados hasta el momento siempre están asociados a un campo

específico del formulario y sólo permite validar un valor. Además, estos validadores sonindependientes del resto de información enviada por el usuario. No obstante, en ocasiones

la validación de un campo depende del contexto o depende del valor de muchos otroscampos. Los ejemplos típicos de validadores globales son los dos campos de contraseña

que deben ser iguales o el campo de fecha de inicio que debe ser anterior que la fecha definalización.

En cualquiera de estos casos, se debe utilizar un validador global que valide los datos

introducidos teniendo en cuenta su contexto. Los validadores globales se pueden establecer antes o después de la validación individual de los campos mediante los pre-validadores y

los post-validadores respectivamente. Normalmente es mejor utilizar un post-validador  porque así los datos ya están validados y limpios, por lo que su formato también se hanormalizado. El listado 2-13 muestra cómo comparar el valor de dos contraseñas mediante

el validador sfValidatorSchemaCompare.

Listado 2-13 - Utilizando el validador sfV alidatorSche maCompare 

$this->validatorSchema->setPostValidator(newsfValidatorSchemaCompare('password1', sfValidatorSchemaCompare::EQUAL,'password2'));

Page 34: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 34/167

 

A partir de Symfony 1.2, también puedes utilizar los operadores habituales de PHP para

realizar las comparaciones en vez de las constantes de la clase

sfValidatorSchemaCompare. Por tanto, el ejemplo anterior es equivalente a:

$this->validatorSchema->setPostValidator(newsfValidatorSchemaCompare('password1', '==', 'password2'));

Sugerencia 

Al igual que el resto de validadores globales, la clase sfValidatorSchemaCompare hereda

del validador sfValidatorSchema. De hecho, el propio sfValidatorSchema es unvalidador global, ya que valida todos los datos introducidos por el usuario pasando el valor 

de cada campo a su validador asociado.

El listado 2-14 muestra cómo emplear un único validador para validar que una fecha deinicio sea anterior a la fecha de finalización y además añade un mensaje de error  personalizado.

Listado 2-14 - Utilizando el validador sfV alidatorSche maCompare 

$this->validatorSchema->setPostValidator(new sfValidatorSchemaCompare('fecha_inicio',sfValidatorSchemaCompare::LESS_THAN_EQUAL, 'fecha_fin',array(),array('invalid' =>'La fecha de inicio ("%left_field%") debe ser anteriora la fecha de finalización ("%right_field%")')));

Utilizar un post-validador asegura que la comparación entre las dos fechas es precisa.

Independientemente del formato en el que se introduce cada fecha, la validación de loscampos fecha_inicio y fecha_fin asegura que sus valores se convierten en un formato

fácilmente comparable (Y-m-d H:i:s por defecto).

Por defecto, los pre-validadores y los post-validadores devuelven errores de tipo global al

formulario. No obstante, algunos de esos validadores pueden asociar un error a un campo

determinado. La opción throw_global_error por ejemplo del validador 

sfValidatorSchemaCompare permite elegir entre un error global (figura 2-10) y un error asociado al primer campo (figura 2-11). El listado 2-15 muestra cómo utilizar la opción

throw_global_error.

Listado 2-15 - Utilizando la opción throw_global_ error 

$this->validatorSchema->setPostValidator(new sfValidatorSchemaCompare('fecha_inicio',sfValidatorSchemaCompare::LESS_THAN_EQUAL, 'fecha_fin',array('throw_global_error' =>true),array('invalid' =>'La fecha de inicio ("%left_field%") debe ser anteriora la fecha de finalización ("%right_field%")'))

Page 35: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 35/167

 

);

Figura 2.10. Error global de un validador global

Figura 2.11. Error local de un validador global

Por último, si se utiliza un validador lógico es posible combinar varios post-validadorescomo muestra el listado 2-16.

Listado 2-16 - Combinando varios post-validadores mediante un validador lógico 

$this->validatorSchema->setPostValidator(new sfValidatorAnd(array(new sfValidatorSchemaCompare('fecha_inicio',sfValidatorSchemaCompare::LESS_THAN_EQUAL, 'fecha_fin'),new sfValidatorSchemaCompare('password1',sfValidatorSchemaCompare::EQUAL, 'password2'),)));

2.7. Subiendo archivos

La gestión de los archivos subidos con PHP, al igual que en otros lenguajes de programación orientados a la web, implica el uso de código HTML y de programación en elservidor. En esta sección se muestran las herramientas ofrecidas por el framework para

simplificar el trabajo del programador y también se muestra cómo evitar los erroreshabituales.

Page 36: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 36/167

 

A continuación se modifica el formulario de contacto para permitir adjuntar archivos al

mensaje. Para ello, se añade un campo llamado archivo, tal y como muestra el listado 2-17.

Listado 2-17 - Añadiendo un campo archivo en el formulario ContactoForm  

// lib/form/ContactoForm.class.phpclass ContactoForm extends sfForm{protected static$asuntos = array('Asunto A', 'Asunto B', 'Asunto C');

publicfunction configure(){$this->setWidgets(array('nombre' =>new sfWidgetFormInput(),'email' =>new sfWidgetFormInput(),'asunto' =>new sfWidgetFormSelect(array('choices' => self::$asuntos)),'mensaje' =>new sfWidgetFormTextarea(),'archivo' =>new sfWidgetFormInputFile(),

));$this->widgetSchema->setNameFormat('contacto[%s]');

$this->setValidators(array('nombre' =>new sfValidatorString(array('required' =>false)),'email' =>new sfValidatorEmail(),'asunto' =>new sfValidatorChoice(array('choices'=>array_keys(self::$asuntos))),'mensaje' =>new sfValidatorString(array('min_length' =>4)),'archivo' =>new sfValidatorFile(),));}}

Cuando se utiliza un widget de tipo sfWidgetFormInputFile para permitir la subida de

archivos, se debe añadir el atributo enctype en la etiqueta <form>, tal y como se muestraen el listado 2-18.

Listado 2-18 - Modificando la plantilla para tener en cuenta el campo archivo 

<form action="<?php echo url_for('contacto/index') ?>" method="POST"enctype="multipart/form-data"><table><?phpecho$formulario?><tr>

<td colspan="2"><input type="submit" /></td></tr></table></form>

Nota 

Page 37: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 37/167

 

Si generas de forma dinámica las plantillas asociadas a los formularios, puedes emplear el

método isMultipart() del objeto del formulario, ya que devuelve true si el formulario

requiere el atributo enctype.

La información de los archivos subidos no se almacena junto con el resto de información

enviada. Por eso, es necesario modificar la llamada al método bind() para pasar esainformación como segundo parámetro, tal y como se muestra en el listado 2-19.

Listado 2-19 - Pasando los archivos subidos al método  bind() 

class contactoActions extends sfActions{publicfunction executeIndex($request){$this->formulario = new ContactoForm();

if($request->isMethod('post')){

$this->formulario->bind($request->getParameter('contacto'), $request->getFiles('contacto'));if($this->formulario->isValid()){$valores = $this->formulario->getValues();// hacer cosas con los valores

// ...}}}

publicfunction executeGracias()

{}}

Aunque el formulario ya es completamente funcional, es necesario modificar la acción para

almacenar el archivo subido en un directorio. Como se explica al principio de este capítulo,

el validador sfValidatorFile convierte toda la información relacionada con el archivo

subido en un objeto de tipo sfValidatedFile. El listado 2-20 muestra cómo utilizar este

objeto para almacenar el archivo subido en el directorio web/uploads.

Listado 2-20 - Utilizando el objeto sfV alidatedFile 

if($this->formulario->isValid()){$archivo = $this->formulario->getValue('archivo');

$nombreArchivo = 'subido_'.sha1($archivo->getOriginalName());$extension = $archivo->getExtension($archivo->getOriginalExtension());$archivo->save(sfConfig::get('sf_upload_dir').'/'.$nombreArchivo.$extension);

Page 38: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 38/167

 

// ...}

La siguiente tabla muestra todos los métodos del objeto sfValidatedFile:

Método Descripción

save()  Guarda el archivo subido

isSaved()  Devuelve true si el archivo se ha guardado

getSavedName()  Devuelve el nombre del archivo guardado

getExtension()  Devuelve la extensión del archivo, en función de su tipo MIME 

getOriginalName()  Devuelve el nombre del archivo subido

getOriginalExtension() Devuelve la extensión del nombre del archivo subido

getTempName()  Devuelve la ruta del archivo temporal

getType()  Devuelve el tipo MIME del archivo

getSize()  Devuelve el tamaño del archivo

Sugerencia 

El tipo MIME que proporciona el navegador por cada archivo subido no es fiable. Paraasegurar la máxima seguridad, durante la validación del archivo se ejecutan de forma

secuencial las funciones finfo_open y mime_content_type y la herramienta file. Sininguna de esas funciones es capaz de determinar el tipo MIME del archivo y si el sistematampoco es capaz de proporcionarlo, se utiliza como último recurso el tipo MIME ofrecido

 por el navegador. Para añadir o modificar las funciones que tratan de averiguar el tipo

MIME, se utiliza la opción mime_type_guessers en el constructor de sfValidatorFile.

Capítulo 3. Formularios para diseñadores web

Los capítulos 1 y 2 muestran cómo crear formularios mediante los widgets y las reglas devalidación. En esos capítulos, los formularios se muestran mediante la instrucción <?php

echo $formulario ?>. Esta instrucción permite a los programadores centrarse en la lógicade la aplicación sin pensar en su aspecto. De esta forma, no es necesario modificar la

 plantilla cada vez que se modifica o se añade un campo. Por todo ello, esta instrucción esmuy útil cuando se crea el prototipo de una aplicación o cuando se está en las fases iniciales

en las que el programador sólo se tiene que centrar en el modelo y en la lógica de negocio.

Page 39: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 39/167

 

Una vez que el desarrollo de la aplicación se estabiliza y después de crear la guía de estilo

del sitio, el diseñador web puede aplicar otro formato a todos los formularios de laaplicación.

Antes de leer este capítulo es obligatorio que conozcas y domines el sistema de plantillas y

la capa de la vista de Symfony. Para ello, puedes leer el capítulo 7 del libro oficial deSymfony 1.1.

Nota 

El sistema de formularios de Symfony cumple con el modelo MVC. El patrón MVC permite desacoplar las tareas del equipo de desarrollo: los programadores crean los

formularios y se encargan de gestionar su lógica y los diseñadores web aplican formato yestilos a los formularios. No obstante, la separación de responsabilidades no implica que ya

no sea necesaria una comunicación fluida entre todos los miembros del equipo.

3.1. Antes de comenzar

En los siguientes ejemplos se va a utilizar el mismo formulario de contacto utilizado en loscapítulos 1 y 2 (ver figura 3-1). Para aquellos diseñadores web que sólo van a leer este

capítulo, a continuación se describen sus características:

y  El formulario dispone de cuatro campos: nombre, email, asunto y mensaje.

y  El formulario se incluye en el módulo contacto.

y  La acción index pasa a la plantilla una variable llamada formulario que representaal formulario completo.

A lo largo de este capítulo se explican las distintas posibilidades que existen para

 personalizar la plantilla que muestra el formulario (listado 3-1).

Page 40: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 40/167

 

Figura 3.1. El formulario de contacto

Listado 3-1 - La plantilla del prototipo utilizado para mostrar el formulario de

contacto 

// apps/frontend/modules/contacto/templates/indexSuccess.php<form action="<?php echo url_for('contacto/index') ?>" method="POST"><table><?php echo $formulario ?><tr><td colspan="2"><input type="submit" /></td></tr></table></form>

Subiendo archivos

Si se incluye en el formulario un campo para subir archivos, es necesario añadir el atributo

enctype en la etiqueta <form>:

<form action="<?php echo url_for('contacto/index') ?>" method="POST"enctype="multipart/data">

El método isMultipart() del objeto $formulario devuelve true si el formulario

requiere incluir ese atributo:

<form action="<?php echo url_for('contacto/index') ?>" method="POST"<?php if($formulario->isMultipart()) { echo 'enctype="multipart/form-

data"' } ?>>

3.2. La plantilla del prototipo

Por el momento, para generar el código HTML que muestra el formulario se ha empleado

la instrucción <?php echo $formulario ?> en la plantilla del prototipo.

Los formularios están compuestos por campos. A su vez, en la plantilla cada campo está

formado por tres elementos:

y  El título del campo o label  

y  La etiqueta del campo (<input>, <select>, etc.)y  O pcionalmente, los mensajes de error del campo

La instrucción <?php echo $formulario ?> genera automáticamente el código HTML detodos esos elementos, como muestra el listado 3-2 en el caso de un formulario enviado con

datos no válidos.

Page 41: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 41/167

 

Listado 3-2 - Plantilla generada automáticamente cuando se envía un formulario con

datos no válidos 

<form action="/frontend_dev.php/contacto" method="POST"><table><tr>

<th><label for="contacto_nombre">Nombre</label></th><td><input type="text" nombre="contacto[nombre]" id="contacto_nombre"/></td></tr><tr><th><label for="contacto_email">Email</label></th><td><ul class="error_list"><li>This email address is invalid.</li></ul><input type="text" nombre="contacto[email]" value="fabien"id="contacto_email" /></td>

</tr><tr>

<th><label for="contacto_asunto">Asunto</label></th><td><select nombre="contacto[asunto]" id="contacto_asunto"><option value="0" selected="selected">Asunto A</option><option value="1">Asunto B</option><option value="2">Asunto C</option></select></td></tr><tr><th><label for="contacto_mensaje">Mensaje</label></th><td>

<ul class="error_list"><li>The mensaje "foo" is too short. It must be of 4 characters atleast.</li></ul><textarea rows="4" cols="30" nombre="contacto[mensaje]"id="contacto_mensaje">foo</textarea></td></tr><tr><td colspan="2"><input type="submit" /></td></tr>

</table></form>

A continuación se explica de forma detallada el código anterior. La figura 3-2 muestra las

filas de tabla (etiqueta <tr>) que se generan por cada campo.

Page 42: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 42/167

 

Figura 3.2. Código HTML generado para cada campo del formulario

Para cada campo de la figura 3-3 se generan tres trozos de código HTML, correspondientes

a los tres elementos que forman cada campo. Para el campo email se generan por ejemploel siguiente código HTML:

y  El título o label :

<label for="contact_email">Email</label>

y  La etiqueta del campo:

Page 43: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 43/167

 

<input type="text" nombre="contacto[email]" value="fabien"id="contacto_email" />

y  Los mensajes de error:

<ul class="error_list">

<li>The email address is invalid.</li></ul>

Figura 3.3. Descomposición del campo del email

Sugerencia 

Todos los campos disponen de un atributo id generado automáticamente, lo que permite alos programadores añadirles estilos CSS o comportamientos JavaScript de forma muy

sencilla.

3.3. Personalizando la plantilla del prototipo

La instrucción <?php echo $formulario ?> puede ser suficiente para los formulariossencillos como el formulario de contacto. En realidad, la instrucción anterior es un atajo de

la instrucción <?php echo $formulario->render() ?> 

Utilizar de forma explícita el método render() permite pasar como argumentos losatributos HTML de cada campo. El listado 3-3 muestra cómo añadir un atributo class al

campo email.

Listado 3-3 - Añadiendo atributos HTML propios con el método rend er() 

<?phpecho$formulario->render(array('email' =>array('class' =>'email')))?>

// Código HTML generado

Page 44: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 44/167

 

<input type="text" nombre="contacto[email]" value=""id="contacto_email"class="email" />

Aunque este método permite personalizar el estilo del formulario, no ofrece la flexibilidadnecesaria para modificar la estructura o layout del formulario.

3.4. Personalizando el diseño

Más allá de la personalización global que permite el método render(), a continuación seexplica cómo mostrar uno a uno los campos del formulario para obtener la máxima

flexibilidad.

3.4.1. Utilizando el método renderRow() en cada campo

La primera forma de conseguir la máxima flexibilidad consiste en generar cada campo de

forma individual. De hecho, la instrucción <?php echo $formulario ?> es equivalente a

llamar al método renderRow() cuatro veces, como se muestra en el listado 3-4.

Listado 3-4 - Utilizando el método rend erRow() 

<form action="<?php echo url_for('contacto/index') ?>" method="POST"><table><?phpecho$formulario['nombre']->renderRow()?><?phpecho$formulario['email']->renderRow()?><?phpecho$formulario['asunto']->renderRow()?><?phpecho$formulario['mensaje']->renderRow()?><tr><td colspan="2"><input type="submit" /></td></tr></table></form>

El acceso a cada campo se realiza utilizando el objeto $formulario como si fuera un array.

El campo email se puede acceder mediante $formulario['email']. El método

renderRow() muestra cada campo como una fila de una tabla HTML. La instrucción

$formulario['email']->renderRow() genera por tanto una fila de tabla para mostrar el

campo email. Para mostrar el formulario completo, se repite el mismo tipo de código para

los otros tres campos asunto, nombre y mensaje.

¿Cómo es posible que un objeto se comporte como un array?

Desde la versión PHP 5, los objetos puede comportarse de forma similar a los arrays. La

clase sfForm implementa el comportamiento ArrayAccess para permitir el acceso a cadacampo mediante una sintaxis corta y muy sencilla. La clave del array es el nombre del

campo y el valor devuelto es el objeto de tipo widget asociado:

Page 45: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 45/167

 

<?phpecho$formulario['email']?>

// Sintaxis que habría que utilizar si sfForm no implementara la interfazArrayAccess<?phpecho$formulario->getField('email')?>

 No obstante, como en la plantilla todas las variables deben ser de sólo lectura, si intentasmodificar el valor del campo se lanza una excepción de tipo LogicException:

<?php$formulario['email'] = ... ?><?phpunset($formulario['email'])?>

La plantilla actual y la plantilla original son funcionalmente indénticas. No obstante,

aunque su aspecto es idéntico, ahora es mucho más fácil personalizar ese aspecto. El

método renderRow() acepta dos argumentos: un array con los atributos HTML y elnombre del título o label . El listado 3-5 utiliza estos dos argumentos para personalizar el

formulario y la figura 3-4 muestra el resultado.

Listado 3-5 - Utilizando los argumentos del método rend erRow() para personalizar el

aspecto del formulario 

<form action="<?php echo url_for('contacto/index') ?>" method="POST"><table><?phpecho$formulario['nombre']->renderRow()?><?phpecho$formulario['email']->renderRow(array('class' =>'email'))?><?phpecho$formulario['asunto']->renderRow()?><?phpecho$formulario['mensaje']->renderRow(array(), 'Tu mensaje')?><tr><td colspan="2"><input type="submit" />

</td></tr>

</table></form>

Page 46: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 46/167

 

Figura 3.4. Personalizando el aspecto del formulario con el método renderRow()

Para generar el campo emaill sólo se utiliza el primer argumento de renderRow():

y  array('class' => 'email') añade la clase emaill a la etiqueta <input> generada.

El campo mensaje se genera de forma similar, pero utiliza los dos argumentos:

y  array() significa que no se quieren añadir atributos HTML propios a la etiqueta

<textarea> generada.

y  "Tu mensaje" permite sustituir el título o label original

Todos los argumentos de renderRow() son opcionales, por lo que no es obligatorio

indicarlos, tal y como sucede en los campos nombre y asunto del ejemplo anterior.

Aunque el método renderRow() permite personalizar los elementos que forman cada

campo, sus posibilidades están limitadas por el código HTML que se utiliza para decorar cada elemento, como se muestra en la figura 3-5.

Figura 3.5. Estructura HTML utilizada por renderRow() y render()

Cómo modificar la estructura utilizada en el prototipo

Symfony utiliza por defecto una tabla HTML para mostrar el formulario. Estecomportamiento se puede modificar utilizando otros formatos específicos, ya sean formatos

definidos por Symfony o formatos creados de forma expresa para el proyecto. En elcapítulo 5 se muestra que para crear un nuevo formato es preciso crear una clase.

Para crear una estructura completamente diferente, existen métodos para generar cada unode los elementos de los campos, como muestra la figura 3-6:

y  renderLabel(): genera el título (etiqueta<label> asociada al campo)

y  render(): genera el campo (etiqueta<input>, <select>, <textarea>, etc.)

Page 47: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 47/167

 

y  renderError(): genera los mensajes de error (en forma de lista de elementos<ul

class="error_list">)

Figura 3.6. Métodos disponibles para personalizar un campo

Cada uno de estos campos se explican detalladamente al final de este capítulo.

3.4.2. Utilizando el método ''render()'' en cada campo

Imagina que ahora se quiere mostrar el formulario en dos columnas. Como se ve en la

figura 3-7, los campos nombre y email se muestran en la misma fila, mientras que los

campos asunto y mensaje se muestran cada uno en su propia fila.

Figura 3.7. Mostrando un formulario en varias columnas

Para conseguir esa estructura es necesario generar de forma individual cada campo del

formulario. Como ya se ha comentado, el objeto formulario permite el acceso a cada

campo como si fuera un array asociativo. El campo email por ejemplo se puede acceder 

mediante $formulario['email']. El listado 3-6 muestra cómo crear un formulario con

dos columnas.

Page 48: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 48/167

 

Listado 3-6 - Mostrar el formulario en dos columnas 

<form action="<?php echo url_for('contacto/index') ?>" method="POST"><table><tr><th>Nombre:</th>

<td><?php echo$formulario['nombre']->render() ?></td><th>Email:</th><td><?php echo$formulario['email']->render() ?></td></tr><tr><th>Asunto:</th><td colspan="3"><?php echo$formulario['asunto']->render() ?></td></tr><tr><th>Mensaje:</th><td colspan="3"><?php echo$formulario['mensaje']->render() ?></td></tr><tr><td colspan="4"><input type="submit" /></td></tr></table></form>

De la misma forma que no es obligatorio indicar explícitamente el método render() sobreel formulario, tampoco es obligatorio indicarlo sobre cada campo, de forma que se puede

reescribir la plantilla como muesta el listado 3-7.

Listado 3-7 - Simplificando el formulario a dos columnas 

<form action="<?php echo url_for('contacto/index') ?>" method="POST"><table><tr><th>Nombre:</th><td><?php echo$formulario['nombre'] ?></td><th>Email:</th><td><?php echo$formulario['email'] ?></td></tr><tr><th>Asunto:</th><td colspan="3"><?php echo$formulario['asunto'] ?></td></tr><tr>

<th>Mensaje:</th><td colspan="3"><?php echo$formulario['mensaje'] ?></td></tr><tr><td colspan="4"><input type="submit" /></td></tr></table></form>

Page 49: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 49/167

 

Al igual que el formulario, cada campo se puede personalizar pasando un array con

atributos HTML al método render(). El listado 3-8 muestra cómo modificar el atributo

class del campo email.

Listado 3-8 - Modificando los atributos HTML con el método rend er() 

<?phpecho$formulario['email']->render(array('class' =>'email'))?>

// Código HTML generado<input type="text" nombre="contacto[email]"class="email"id="contacto_email" />

3.4.3. Utilizando el método ''renderLabel()'' en cada campo

En el ejemplo anterior no se han generado títulos o labels durante la personalización del

formulario. El listado 3-9 utiliza el método renderLabel() para generar un título para cadacampo.

Listado 3-9 - Utilizando el método rend erLabel() 

<form action="<?php echo url_for('contacto/index') ?>" method="POST"><table><tr><th><?php echo$formulario['nombre']->renderLabel()?>:</th><td><?php echo$formulario['nombre'] ?></td><th><?php echo$formulario['email']->renderLabel()?>:</th><td><?php echo$formulario['email'] ?></td></tr><tr><th><?php echo$formulario['asunto']->renderLabel()?>:</th>

<td colspan="3"><?php echo$formulario['asunto'] ?></td></tr><tr><th><?php echo$formulario['mensaje']->renderLabel()?>:</th><td colspan="3"><?php echo$formulario['mensaje'] ?></td></tr><tr><td colspan="4"><input type="submit" /></td></tr></table></form>

El valor de la etiqueta <label> se genera automáticamente a partir del nombre del campo.

 No obstante, también es posible utilizar un título propio si se pasa como argumento al

método renderLabel(), como se muestra en el listado 3-10.

Listado 3-10 - Modificando el título del campo 

<?phpecho$formulario['mensaje']->renderLabel('Tu mensaje')?>

Page 50: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 50/167

 

// Código HTML generado<label for="contacto_mensaje">Tu mensaje</label>

¿Por qué se utiliza el método renderLabel() pasándole el título como argumento? ¿Por 

qué no se utiliza una etiqueta <label> de HTML en su lugar? El motivo es que el método

renderLabel() genera una etiqueta <label> y le añade de forma automática un atributo

for para asociarlo al campo de formulario mediante el atributo id. De esta forma teaseguras que el campo sea accesible ya que cuando se pulsa sobre la etiqueta, el campo seselecciona de forma automática:

<labelfor="contacto_email">Email</label><inputtype="text" nombre="contacto[email]"id="contacto_email" />

Además, se pueden añadir atributos HTML pasando un segundo argumento al método

renderLabel():

<?phpecho$formulario['enviar_notificacion']->renderLabel(null,

array('class' =>'inline'))?>

// Código HTML generado<label for="contacto_enviar_notificacion"class="inline">Enviarnotificacion</label>

En el ejemplo anterior, el primer argumento del método es null, por lo que se utiliza la

generación automática del texto del título.

3.4.4. Utilizando el método ''renderError()'' en cada campo

La plantilla, tal y como está definida ahora mismo, no tiene en cuenta los mensajes de error.

El listado 3-11 vuelve a mostrar los errores utilizando el método renderError().

Listado 3-11 - Mostrando los mensjes de error con el método rend erError() 

<form action="<?php echo url_for('contacto/index') ?>" method="POST"><table><tr><th><?php echo$formulario['nombre']->renderLabel()?>:</th><td><?phpecho$formulario['nombre']->renderError()?><?phpecho$formulario['nombre']?></td>

<th><?php echo$formulario['email']->renderLabel()?>:</th><td>

<?phpecho$formulario['email']->renderError()?><?phpecho$formulario['email']?></td></tr><tr><th><?php echo$formulario['asunto']->renderLabel()?>:</th><td colspan="3"><?phpecho$formulario['asunto']->renderError()?>

Page 51: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 51/167

 

<?phpecho$formulario['asunto']?></td></tr><tr><th><?php echo$formulario['mensaje']->renderLabel()?>:</th><td colspan="3"><?phpecho$formulario['mensaje']->renderError()?><?phpecho$formulario['mensaje']?></td></tr><tr><td colspan="4"><input type="submit" /></td></tr></table></form>

3.4.5. Personalizando los mensajes de error hasta el último detalle

El método renderError() genera una lista con los errores asociados al campo del

formulario. Sólo genera código HTML si el campo tiene al menos un error. Por defecto, la

lista generada es de tipo lista no ordenada de HTML (<ul>).

Aunque este comportamiento es adecuado la mayoría de las veces, existen dos métodos

llamados hasError() y getError() que permiten el acceso directo a todos los errores. El

listado 3-2 muestra cómo personalizar los mensajes de error del campo email.

Listado 3-12 - Accediendo a los mensajes de error 

<?phpif($formulario['email']->hasError()): ?>

<ul class="error_list"><?phpforeach($formulario['email']->getError()as$error): ?><li><?php echo$error ?></li><?phpendforeach; ?></ul><?phpendif; ?>

El código del ejemplo anterior genera exactamente el mismo código HTML que la llamada

al método renderError().

3.4.6. Trabajando con campos ocultos

En los siguientes ejemplos se supone que existe en el formulario un campo oculto

obligatorio llamado origen. Este campo almacena la página desde la que ha llegado el

usuario hasta el formulario. La instrucción <?php echo $formulario ?> genera el códigoHTML de los campos ocultos y lo añade después del último campo visible, tal y como se

muestra en el listado 3-13.

Listado 3-13 - Generando el código HTML de los campos ocultos 

Page 52: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 52/167

 

<tr><th><label for="contacto_mensaje">Mensaje</label></th><td><textarea rows="4" cols="30" nombre="contacto[mensaje]"id="contacto_mensaje"></textarea><input type="hidden" nombre="contacto[origen]" id="contacto_origen" /></td></tr>

Como se observa en el código generado para el campo oculto origen, sólo se genera la

 parte correspondiente a la etiqueta <input type="hidden">. O bviamente, no tiene sentido

generar el título o label de un campo oculto. En cuanto a los posibles errores de este campo,aunque se trata de un campo oculto, es posible que se produzcan errores debidos a cualquier 

 problema en el código. Aunque estos errores no se asocian directamente al campo origen,se añaden a los errores globales. En el capítulo 5 se muestran otros casos en los que seutilizan errores globales. La figura 3-8 muestra cómo se visualizan los mensajes de error 

que se producen en el campo origen y el listado 3-14 muestra el código generado para esoserrores.

Figura 3.8. Mostrando los mensajes de error globales

Listado 3-14 - Generando mensajes de error globales 

<tr><tdcolspan="2"><ulclass="error_list"><li>Origen: Required.</li></ul></td></tr>

Cuidado 

Page 53: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 53/167

 

Cuando personalices el diseño de un formulario, no olvides incluir los campos ocultos y losmensajes de error globales.

3.4.7. Trabajando con los errores globales

Los formularios disponen de tres tipos de error:

y  Errores asociados a un campo específico

y  Errores globales

y  Errores de campos ocultos (<input type="hidden">) o de campos que no se muestran

en el formulario. Estos errores se añaden a los errores globales.

En las secciones anteriores ya se ha visto cómo tratar los errores asociados a los campos yel listado 3-15 muestra cómo tratar los mensajes de error globales.

Listado 3-15 - Trabajando con mensajes de error globales 

<form action="<?php echo url_for('contacto/index') ?>" method="POST"><table><tr><td colspan="4"><?phpecho$formulario->renderGlobalErrors()?></td></tr>

// ...</table>

La llamada al método renderGlobalErrors() muestra la lista de errores globales.

También es posible acceder de forma individual a los errores globales utilizando losmétodos hasGlobalErrors() y getGlobalErrors(), como se muestra en el listado 3-16.

Listado 3-16 - Personalizando los errores globales con los métodos

hasGlobalErrors() y getGlobalErrors() 

<?phpif($formulario->hasGlobalErrors()): ?><tr><td colspan="4"><ul class="error_list"><?phpforeach($formulario->getGlobalErrors()as$nombre =>$error): ?><li><?php echo$nombre.': '.$error ?></li>

<?phpendforeach; ?></ul></td></tr><?phpendif; ?>

Cada error global dispone de un nombre ($nombre en el código anterior) y un mensaje

($error en el código anterior). Si el error es realmente global, el nombre siempre está

Page 54: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 54/167

 

vacío, pero si el error global tiene su origen en un campo oculto, el nombre es realmente el

título o label del campo oculto.

Aunque la plantilla actual (figura 3-8) es equivalente a la plantilla con la que empezó estecapítulo, el diseño de la nueva plantilla es completamente personalizable.

Figura 3.9. Formulario personalizado que utiliza los métodos de los campos

3.5. InternacionalizaciónTodos los elementos del formulario, desde los títulos hasta los mensajes de error, utilizande forma automática el sistema de internacionalización de Symfony. Por lo tanto, el

diseñador web no tiene que añadir nada especial a las plantillas para poder internacionalizar los formularios, incluso aunque establezca el título de los campos con el método

renderLabel(). La traducción de todos los textos se realiza de forma automática. Elcapítulo 9 incluye información mucho más detallada de la internacionalización de los

formularios.

3.6. Trabajando con el programador

Para concluir este capítulo, se describe el escenario de trabajo típico cuando se desarrollan

formularios con Symfony:

y  El equipo de desarrollo crea la clase del formulario y su acción. La plantilla no es

nada más que la instrucción <?php echo $formulario ?> 

Page 55: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 55/167

 

y  Mientras tanto, los diseñadores crean la guía de estilo de la aplicación y las reglas

que se van a aplicar a los formularios: estructura global, visualización de losmensajes de error, etc.

y  Una vez que la lógica de negocio está terminada y que la guía de estilo ha sidoaprobada, el equipo de diseño modifica las plantillas de los formularios. Lo único

que deben conocer los diseñadores es el nombre de los campos y la acción que seencarga de procesar el formulario.

Una vez concluida esta primera fase, ya es posible modificar la lógica de negocio y el

aspecto de las plantillas de forma simultánea.

El equipo de desarrollo puede realizar varias tareas sin que afecten a las plantillas y por 

tanto, sin la necesidad de que intervenga un diseñador:

y  Modificar los widgets del formularioy  Personalizar los mensajes de error 

y Añadir, modificar o borrar reglas de validación

Igualmente, el equipo de diseño puede realizar todos los cambios estéticos que desee sin

requerir la intervención del equipo de desarrollo.

 No obstante, las siguientes acciones requieren la coordinación de los dos equipos:

y  Modificar el nombre de un campoy  Añadir o eliminar campos

La coordinación en las tareas anteriores es lógica ya que implican cambios tanto en la

 programación como en la visualización del formulario. Como se indica al inicio de estecapítulo, el mecanismo de formularios permite la división de las tareas, pero lacomunicación entre los equipos de desarrollo y diseño sigue siendo imprescindible.

Capítulo 4. Integración con Propel

En los proyectos web, la mayoría de formularios se utilizan para crear o modificar objetosdel modelo de datos. Este tipo de objetos luego se guardan en una base de datos mediante

un ORM. El sistema de formularios de Symfony dispone de una capa adicional que sirve deinterfaz con Propel, el ORM incluido en Symfony. Gracias a esta interfaz, es muy sencillo

crear formularios basados en estos objetos del modelo.

Este capítulo explica detalladamente cómo integrar los formularios y los objetos Propel.Por ese motivo, es muy recomendable que conozcas y domines el uso de Propel y su

integración con Symfony. Para ello, puedes leer el capítulo 8 del libro oficial de Symfony1.1.

4.1. Antes de comenzar

Page 56: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 56/167

 

En los ejemplos de este capítulo se crea un sistema para gestionar artículos (como por 

ejemplo los artículos de un blog). A continuación se muestra el esquema de la base de

datos, que está formado por cinco tablas: articulo, autor, categoria, etiqueta y

articulo_etiqueta.

Listado 4-1 - Esquema de la base de datos 

// config/schema.ymlpropel:articulo:id: ~titulo: { type: varchar(255), required: true }slug: { type: varchar(255), required: true }

contenido: longvarcharestado: varchar(255)autor_id: { type: integer, required: true, foreignTable:

autor, foreignReference: id, onDelete: cascade }categoria_id: { type: integer, required: false, foreignTable:

categoria, foreignReference: id, onDelete: setnull }fecha_publicacion: timestampcreated_at: ~updated_at: ~_uniques:unique_slug: [slug]

autor:id: ~nombre: varchar(20)apellidos: varchar(20)

email: { type: varchar(255), required: true }activo: boolean

categoria:id: ~nombre: { type: varchar(255), required: true }

etiqueta:id: ~nombre: { type: varchar(255), required: true }

articulo_etiqueta:articulo_id: { type: integer, foreignTable: articulo,

foreignReference: id, primaryKey: true, onDelete: cascade }etiqueta_id: { type: integer, foreignTable: etiqueta,

foreignReference: id, primaryKey: true, onDelete: cascade }

Las relaciones entre las tablas son las siguientes:

y  Relación 1-n entre las tablas articulo y autor: cada artículo está escrito por uno ysólo un autor.

y  Relación 1-n entre las tablas articulo y categoria: cada artículo está asociado acero o una categoría.

y  Relación n-n entre las tablas articulo y etiqueta.

Page 57: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 57/167

 

4.2. Generando las clases del formulario

En los siguientes ejemplos se va a modificar la información de las tablas articulo, autor,

categoria y etiqueta. Para ello, se crean formularios asociados a cada una de las tablas yse configuran los widgets y validadores relacionados con el esquema de la base de datos.

Aunque es posible crear estos formularios a mano, se trata de una tarea tediosa y pesada,además de que obliga a repetir la misma información en varios archivos (nombre de

columans y campos, máximo tamaño de columnas y campos, etc.) Además, si losformularios se hacen a mano, cada vez que se modifica el modelo, se debe modificar la

clase del formulario asociado. Afortunadamente, el plugin de Propel incluye una tarea

llamada propel_build-forms que automatiza todo este proceso y genera los formulariosrelacionados con un objeto del modelo de datos:

$ ./symfony propel:build-forms

Cuando se generan los formularios, esta tarea crea una clase por cada tabla y le añade los

validadores y widgets necesarios para cada columna teniendo en cuenta la informacióndisponible en el modelo y la relación entre las tablas.

Nota 

Las tareas propel:build-all y propel:build-all-load también actualizan las clases de

los formularios, ya que invocan de forma automática la clase propel:build-forms.

Después de ejecutar esta tarea, se crea una estructura de archivos en el directorio

lib/form/. Considerando el modelo de datos del ejemplo anterior, se crea la siguiente

estructura de archivos:

lib/ form/ BaseFormPropel.class.phpArticuloForm.class.phpArticuloEtiquetaForm.class.phpAutorForm.class.phpCategoriaForm.class.phpEtiquetaForm.class.phpbase/ BaseArticuloForm.class.phpBaseArticuloEtiquetaForm.class.phpBaseAutorForm.class.php

BaseCategoriaForm.class.phpBaseEtiquetaForm.class.php

La tarea propel:build-forms genera dos clases para cada tabla del esquema, una en el

directorio lib/form/base y la otra en el directorio lib/form/. Para la tabla autor por 

ejemplo, se generan las clases BaseAutorForm y AutorForm que se guardan

respectivamente en los archivos lib/form/base/BaseAutorForm.class.php y

lib/form/AutorForm.class.php.

Page 58: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 58/167

 

Directorio de generación de los formularios

La tarea propel:build-forms genera sus archivos con una estructura similar a la

estructura utilizada por Propel. El atributo package del esquema de datos de Propel permite

agrupar de forma lógica diferentes tablas. El paquete por defecto es lib.model, por lo que

Propel genera sus archivos en el directorio lib/model/ y las clases de los formularios segeneran en el directorio lib/form. si se emplea el valor lib.model.cms en el atributo

package como se muestra en el siguiente ejemplo, las clases de Propel se generan en el

directorio lib/model/cms/ y las clases del formulario se generan en el directorio

lib/form/cms/.

propel:_attributes: { noXsd: false, defaultIdMethod: none, package:

lib.model.cms }# ...

Como se verá en el capítulo 5, los paquetes son muy útiles para dividir el esquema de la

 base de datos y para incluir formularios en los plugins.

El capítulo 8 del libro oficial de Symfony 1.1 dispone de más información sobre los

 paquetes de Propel.

La siguiente tabla muestra la jerarquía de las diferentes clases relacionadas con la

definición del formulario AutorForm.

Clase PaqueteQuién trabaja 

con ella Descripción

AutorForm  Proyecto El programadorRedefine el formulario generado

automáticamente

BaseAutorForm  Proyecto SymfonyBasado en el esquema y generado cada vez que

se ejecuta la tarea propel:build-forms 

BaseFormPropel Proyecto El programadorPermite personalizar de forma global los

formularios Propel

sfFormPropel Plugin de

PropelSymfony Base de todos los formularios Propel

sfForm  Symfony Symfony Base de todos los formularios Symfony

Para crear o modificar un objeto de la clase Autor, se utiliza la clase AutorForm que semuestra en el listado 4-2. Como se puede observar, esta clase no contiene ningún método,

ya que hereda de BaseAutorForm, que es la clase que se genera automáticamente en

Page 59: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 59/167

 

función de la configuración. La clase AutorForm es la clase que se utiliza para personalizar y redefinir la configuración del formulario.

Listado 4-2 - Clase  AutorForm  

class AutorForm extends BaseAutorForm{publicfunction configure(){}}

El listado 4-3 muestra el contenido de la clase BaseAutorForm con los validadores y

widgets que se han creado automáticamente mediante la introspección de la tabla autor del

modelo de datos.

Listado 4-3 - Clase Base AutorForm que representa el formulario de la tabla autor 

class BaseAutorForm extends BaseFormPropel{publicfunction setup(){$this->setWidgets(array('id' =>new sfWidgetFormInputHidden(),'nombre' =>new sfWidgetFormInput(),'apellidos' =>new sfWidgetFormInput(),'email' =>new sfWidgetFormInput(),));

$this->setValidators(array('id' =>new sfValidatorPropelChoice(array('model' =>'Autor',

'column' =>'id', 'required' =>false)),'nombre' =>new sfValidatorString(array('max_length' =>20, 'required'=>false)),'apellidos' =>new sfValidatorString(array('max_length' =>20, 'required'=>false)),'email' =>new sfValidatorString(array('max_length' =>255)),));

$this->widgetSchema->setNameFormat('autor[%s]');

$this->errorSchema = new sfValidatorErrorSchema($this->validatorSchema);

parent::setup();

}

publicfunction getModelName(){return'Autor';}}

Page 60: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 60/167

 

La clase generada automáticamente es muy parecida a los formularios que se han creado en

los capítulos anteriores, con las siguientes excepciones:

y  La clase base es BaseFormPropel en vez de sfForm 

y  La configuración de los validadores y de los widgets se realiza en el método setup(), en

vez de en el métodoconfigure()

 

y  El método getModelName() devuelve el nombre de la clase Propel relacionada con el

formulario

Personalizar los formularios Propel de forma global

Además de las clases generadas para cada tabla, la tarea propel:build-forms genera una

clase llamada BaseFormPropel. Esta clase inicialmente está vacía y es la clase base de

todas las demás clases que se encuentran en el directorio lib/form/base/. Gracias a estaclase es posible configurar de forma global el comportamiento de todos los formularios dePropel. El siguiente ejemplo muestra cómo modificar el formato de todos los formularios

Propel:

abstract class BaseFormPropel extends sfFormPropel{publicfunction setup(){

sfWidgetFormSchema::setDefaultFormFormatterName('div');}}

Además, la clase BaseFormPropel hereda de la clase sfFormPropel. Esta última clase

incluye funcionalidades específicas de Propel, como por ejemplo el proceso de almacenar en la base de datos la información enviada por el usuario.

Sugerencia 

Las clases base utilizan el método setup() en vez de configure() para realizar su

configuración. De esta forma es posible redefinir la configuración de las clases generadas

vacías sin tener que incluir la instrucción parent::configure() 

Los nombres de los campos del formulario son idénticos a los nombres de las columnas del

esquema de datos: id, nombre, apellidos y email.

Para cada columna de la tabla autor, la tarea propel:build-forms genera un widget y unvalidador que cumplan con la definición del esquema de datos. Esta tarea siempre genera

los validadores más seguros que sean posible. Si se considera por ejemplo el campo id, se

 podría validar solamente que sea un número entero. Sin embargo, el validador generadoautomáticamente también comprueba que ese identificador existe (cuando se quiere

modificar un objeto existente) o que ese identificador no exista (cuando se quiere crear unnuevo objeto). De esta forma, el validador utilizado es mucho más seguro que simplemente

comprobar que es un número entero.

Page 61: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 61/167

 

Los formularios generados de forma automática están listos para utilizarse sin realizar 

ningún cambio. Si se añade la instrucción <?php echo $formulario ?> en la plantilla, yase dispone de un formulario completamente funcional y con validación de datos sin tener que escribir ni una sola línea de código.

Además de la posibilidad de crear prototipos de forma muy rápida, los formulariosgenerados automáticamente son fáciles de modificar sin tener que editar las clasesgeneradas. La razón es la herencia de las clases base y las clases del formulario.

Cada vez que se modifica el esquema de la base de datos, esta tarea permite volver a

generar los formularios para que tengan en cuenta todas las modificaciones realizadas y sin perder toda la configuración realizada en los formularios.

4.3. El generador CRUD

Después de generar todas las clases del formulario, ahora se muestra cómo crear un módulo

de Symfony que permita trabajar con todos los objetos a través del navegador web. En lossiguientes ejemplos se muestra cómo crear, modificar y borrar objetos de las clases

Articulo, Autor, Categoria y Etiqueta.

En primer lugar se crea el modelo de la clase Autor. Aunque el módulo se puede crear a

mano, el plugin de Propel incluye una tarea llamada propel:generate-crud que generaun módulo de tipo CRUD basado en una clase del modelo de objetos de Propel:

$ ./symfony propel:generate-crud frontend autor Autor

La tarea propel:generate-crud requiere tres argumentos:

y  frontend: nombre de la aplicación en la que se va a crear el módulo

y  autor: nombre del módulo que se va a crear

y  Autor: nombre de la clase del modelo en la que se basa el módulo

Nota 

El acrónimo CRUD significa C reation / Retrieval / Update / Deletion (Crear / O btener /

Modificar / Borrar) que son las cuatro operaciones básicas que se realizan sobre lainformación del modelo de datos.

En el listado 4-4 se muestran las cinco acciones que genera la tarea sobre la clase Autor:listar la información (index), crear nuevos objetos (create), modificar los objetos

existentes (edit), guardar los cambios realizados (update) y borrar los objetos (delete).

Listado 4-4 - La clase autorActions generada por la tarea 

// apps/frontend/modules/autor/actions/actions.class.phpclass autorActions extends sfActions

Page 62: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 62/167

 

{publicfunction executeIndex(){$this->autorList = AutorPeer::doSelect(new Criteria());}

publicfunction executeCreate(){$this->form = new AutorForm();

$this->setTemplate('edit');}

publicfunction executeEdit($request){$this->form = new AutorForm(AutorPeer::retrieveByPk($request->getParameter('id')));}

publicfunction executeUpdate($request)

{$this->forward404Unless($request->isMethod('post'));

$this->form = new AutorForm(AutorPeer::retrieveByPk($request->getParameter('id')));

$this->form->bind($request->getParameter('autor'));if($this->form->isValid()){$autor = $this->form->save();

$this->redirect('autor/edit?id='.$autor->getId());}

$this->setTemplate('edit');}

publicfunction executeDelete($request){$this->forward404Unless($autor = AutorPeer::retrieveByPk($request->getParameter('id')));

$autor->delete();

$this->redirect('autor/index');}}

En este módulo, el flujo de trabajo del formulario está definido por los métodos create,

edit y update. También es posible indicar a la tarea propel:generate-crud que genere

sólo un método que incluya las tres funcionalidades anteriores, mediante la opción --non-

atomic-actions:

$ ./symfony propel:generate-crud frontend autor Autor --non-atomic-actions

Page 63: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 63/167

 

El código generado mediante la opción --non-atomic-actions (ver listado 4-5) es muchomás conciso.

Listado 4-5 - La clase autorActions generada con la opción --non-atomic-actions 

class autorActions extends sfActions{publicfunction executeIndex(){$this->autorList = AutorPeer::doSelect(new Criteria());}

publicfunction executeEdit($request){$this->form = new AutorForm(AutorPeer::retrieveByPk($request->getParameter('id')));

if($request->isMethod('post')){

$this->form->bind($request->getParameter('autor'));if($this->form->isValid()){$autor = $this->form->save();

$this->redirect('autor/edit?id='.$autor->getId());}}}

publicfunction executeDelete($request){$this->forward404Unless($autor = AutorPeer::retrieveByPk($request-

>getParameter('id')));

$autor->delete();

$this->redirect('autor/index');}}

Esta tarea también genera dos plantillas, indexSuccess y editSuccess. La plantilla

editSuccess generada no utiliza la instrucción <?php echo $form ?>. Para modificar 

este comportamiento, se utiliza la opción --non-verbose-templates:

$ ./symfony propel:generate-crud frontend autor Autor --non-verbose-templates

Esta opción es muy útil mientras se construye el prototipo de la aplicación, tal y como

muestra el listado 4-6.

Listado 4-6 - La plantilla editSuccess 

// apps/frontend/modules/autor/templates/editSuccess.php

Page 64: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 64/167

 

<?php$autor = $form->getObject()?><h1><?php echo$autor->isNew() ? 'New' : 'Edit'?> Autor</h1>

<form action="<?php echo url_for('autor/edit'.(!$autor->isNew() ?'?id='.$autor->getId() : '')) ?>" method="post"<?php$form->isMultipart()and print'enctype="multipart/form-data" ' ?>><table><tfoot><tr><td colspan="2">&nbsp;<a href="<?php echo url_for('autor/index') ?>">Cancel</a><?phpif(!$autor->isNew()): ?>&nbsp;<?php echo link_to('Delete', 'autor/delete?id='.$autor->getId(),array('post' =>true, 'confirm' =>'Are you sure?'))?><?phpendif; ?><input type="submit" value="Save" /></td></tr></tfoot><tbody>

<?phpecho$form?></tbody></table></form>

Sugerencia 

La opción --with-show permite generar una acción y una plantilla para visualizar objetos

(sólo en modo lectura).

Si ahora se accede a la dirección /frontend_dev.php/autor, ya se puede utilizar elmódulo generado (figuras 4-1 y 4-2). Juega un poco con la interfaz y añade algo deinformación. El módulo generado permite listar, añadir, modificar y borrar autores.

Además, puedes comprobar que las reglas de validación también se aplican a losformularios.

Figura 4.1. Listado de autores

Figura 4.2. Modificando los datos de un autor con errores de validación

A continuación se realiza la misma operación sobre la clase Articulo:

$ ./symfony propel:generate-crud frontend articulo Articulo --non-verbose-templates --non-atomic-actions

Page 65: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 65/167

 

El código generado es muy parecido al código de la clase Autor. Sin embargo, si se intentacrear un artículo nuevo, el código lanza un error fatal que se muestra en la figura 4-3.

Figura 4.3. Las tablas relacionadas deben definir el método __toString()

El formulario ArticuloForm utiliza el widget sfWidgetFormPropelSelect para

representar la relación entre los objetos Articulo y Autor. Este widget crea una lista

desplegable con todos los autores disponibles. Para mostrar los autores, los objetos de tipoAutor se convierten en una cadena de texto mediante el método mágico __toString(),

que debe estar definido en la clase Autor, como se muestra en el listado 4-7.

Listado 4-7 - Añadiendo el método  __toString() en la clase  Autor 

class Autor extends BaseAutor{publicfunction __toString(){return$this->getNombre().' '.$this->getApellidos();}

}

Siguiendo el ejemplo de la clase Autor, también se pueden crear métodos __toString() 

en las clases Articulo, Categoria y Etiqueta.

Sugerencia 

La opción method del widget sfWidgetFormPropelSelect permite establecer el nombre

del método que se utiliza para convertir el objeto en texto.

La figura 4-4 muestra cómo crear un artículo después de incluir el método __toString() 

en la clase Autor.

Page 66: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 66/167

 

 

Figura 4.4. Creando un artículo

Puedes enviarnos un comentario con sugerencias, críticas o para informarnos de algún error.

4.4. Personalizando los formularios generados

automáticamente

Las tareas propel:build-forms y propel:generate-crud permite crear módulos de

Symfony preparados para listar, crear, modificar y borrar objetos del modelo. Además,estos modelos tienen en cuenta las reglas de validación del modelo y también la relación

entre tablas. Lo mejor de todo es que el programador no tiene que escribir ni una sola líneade código.

El siguiente paso consiste en personalizar el código generado automáticamente. Aunque lasclases del formulario ya tienen en cuenta muchos elementos, normalmente es necesario

 personalizar algunos aspectos del formulario.

4.5. Configurando los validadores y los widgets

En esta sección se muestra cómo configurar los validadores y los widgets generados por 

defecto.

Page 67: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 67/167

 

El formulario ArticuloForm dispone de un campo llamado slug. Un slug es una cadena decaracteres que representa de forma única al artículo dentro de la URL. Si se dispone por 

ejemplo de un artículo cuyo título es "O ptimizando la programación con Sym f  ony" y cuyo

id es 12, su slug es 12-optimizando-la-programacion-con-symfony . Este campo se

determina automáticamente a partir del campo titulo cada vez que se guarda el objeto,

 pero también se permite al usuario establecerlo de forma explícita. Aunque este campo esobligatorio según el esquema de datos, no puede ser un campo obligatorio en el formulario.

Por este motivo se modifica el validador para hacer el campo opcional, como se muestra en

el listado 4-8. Además, se personaliza el campo contenido aumentando su tamaño yobligando al usuario a escribir al menos cinco caracteres.

Listado 4-8 - Personalizando los validadores y los widgets 

class ArticuloForm extends BaseArticuloForm{publicfunction configure(){

// ...

$this->validatorSchema['slug']->setOption('required', false);$this->validatorSchema['contenido']->setOption('min_length', 5);

$this->widgetSchema['contenido']->setAttributes(array('rows' =>10, 'cols'=>40));}}

En el código anterior se utilizan los objetos validatorSchema y widgetSchema como sifueran arrays de PHP. Estos arrays aceptan el nombre del campo como clave y devuelven

respectivamente el objeto del validador y el objeto del widget. Por lo tanto, es posible personalizar cada campo y cada widget.

Nota 

Para poder acceder a los objetos como si fueran arrays de PHP, las clases

sfValidatorSchema y sfWidgetFormSchema implementan la interfaz ArrayAccess,disponible desde la versión PHP 5.

Para asegurar que dos artículos no tengan el mismo slug, la definición del esquema incluyela restricción para hacer el slug único. Esta restricción a nivel de base de datos, se refleja en

el formulario ArticuloForm a través del validador sfValidatorPropelUnique. Estevalidador se puede emplear para asegurar que el valor de un campo sea único en la base de

datos, lo que puede ser útil para campos como el login o el email. El listado 4-9 muestra

cómo utilizarlo en el formulario ArticuloForm.

Listado 4-9 - Utilizando el validador sfV alidatorPropelUnique para asegurar que el

valor de un campo sea único 

class BaseArticuloForm extends BaseFormPropel

Page 68: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 68/167

 

{publicfunction setup(){// ...

$this->validatorSchema->setPostValidator(new sfValidatorPropelUnique(array('model' =>'Articulo', 'column'=>array('slug'))));}}

El validador sfValidatorPropelUnique se establece como post-validador para que se

ejecute después de comprobar que toda la información enviada por el usuario es válida.

Para validar que el campo slug sea único, el validador no sólo tiene que accceder a su

valor, sino que también debe acceder al valor de la clave o claves primarias. Además, lasreglas de validación son diferentes en la creación y en la modificación del objeto, ya que el

 slug puede permanecer invariante durante la modificación de un artículo.

A continuación se modifica el campo activo de la tabla autor, que se utiliza para

determinar si un usuario es activo o no. El listado 4-10 muestra cómo se pueden excluir los

autores inactivos en el formulario ArticuloForm modificando la opción criteria del

widget sfWidgetPropelSelect asociado al campo autor_id. La opción criteria toma

como valor un objeto de tipo Criteria de Propel, lo que permite restringir el número deelementos de la lista desplegable.

Listado 4-10 - Personalizando el widget sfW idgetPropelSelect 

class ArticuloForm extends BaseArticuloForm{publicfunction configure(){// ...

$autorCriteria = new Criteria();$autorCriteria->add(AutorPeer::ACTIVO, true);

$this->widgetSchema['autor_id']->setOption('criteria', $autorCriteria);}}

El código anterior sólo modifica la lista de opciones disponibles en el widget, pero también

se deben restringir las opciones en el validador, tal y como se muestra en el listado 4-11. Aligual que el widget sfWidgetProperSelect, el validador sfValidatorPropelChoice 

acepta una opción llamada criteria para restringir las opciones válidas para este campo.

Listado 4-11 - Personalizando el validador sfV alidatorPropelChoice 

class ArticuloForm extends BaseArticuloForm{publicfunction configure()

Page 69: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 69/167

 

{// ...

$autorCriteria = new Criteria();$autorCriteria->add(AutorPeer::ACTIVO, true);

$this->widgetSchema['autor_id']->setOption('criteria', $autorCriteria);$this->validatorSchema['autor_id']->setOption('criteria',$autorCriteria);}}

En el ejemplo anterior se define el objeto Criteria directamente en el método

configure(). En un proyecto real, este objeto Criteria puede ser muy útil en otras partes

del código, por lo que es mejor crear un método llamado getCriteriaAutoresActivos() 

dentro de la clase AutorPeer y llamar a este método desde ArticuloForm como muestra el

listado 4-12.

Listado 4-12 - Refactorizando el objeto Criteria en la clase del modelo 

class AutorPeer extends BaseAutorPeer{staticpublicfunction getCriteriaAutoresActivos(){$criteria = new Criteria();$criteria->add(AutorPeer::ACTIVO, true);

return$criteria;}}

class ArticuloForm extends BaseArticuloForm{publicfunction configure(){$autorCriteria = AutorPeer::getCriteriaAutoresActivos();$this->widgetSchema['autor_id']->setOption('criteria', $autorCriteria);$this->validatorSchema['autor_id']->setOption('criteria',$autorCriteria);}}

Sugerencia 

Mientras que el widget sfWidgetPropelSelect y el validador 

sfValidatorPropelChoice representan una relación 1-n entre dos tablas, el widgetsfWidgetFormPropelSelectMany y el validador sfValidatorPropelChoiceMany representan una relación de tipo n-n y aceptan las mismas opciones. En el formulario

ArticuloForm, estas clases se emplean para representar la relación entre las tablas

articulo y etiqueta.

Page 70: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 70/167

 

4.5.1. Modificando el validador

El campo email se define en el esquema como varchar(255), por lo que Symfony crea un

validador de tipo sfValidatorString() para restringir su longitud a un máximo de 255 caracteres. Como este campo requiere que el valor introducido sea una dirección válida de

correo eletrónico, el listado 4-14 reemplaza el validador generado por un validador de tiposfValidatorEmail.

Listado 4-13 - Modificando el validador del campo e mail del formulario  AutorForm  

class AutorForm extends BaseAutorForm{publicfunction configure(){$this->validatorSchema['email'] = new sfValidatorEmail();}}

4.5.2. Añadiendo un validador

En la sección anterior se explica cómo modificar un validador generado automáticamente.

Pero en el caso del campo emaill, también es necesario mantener el validador que limita el

máximo número de caracteres. El listado 4-14 utiliza el validador sfValidatorAnd paragarantizar que la dirección de email sea válida y que su longitud sea igual o inferior almáximo establecido.

Listado 4-14 - Utilizando un validador múltiple 

class AutorForm extends BaseAutorForm

{publicfunction configure(){$this->validatorSchema['email'] = new sfValidatorAnd(array(new sfValidatorString(array('max_length' =>255)),new sfValidatorEmail(),));}}

El ejemplo anterior tiene un inconveniente, ya que si se modifica el tamaño del campo

email en el esquema de la base de datos, es preciso acordarse de que también hay que

modificarlo en el formulario. Por lo tanto, en vez de reemplazar el validador generadoautomáticamente, es mejor añadir otro validador como se muestra en el listado 4-15.

Listado 4-15 - Añadiendo un validador 

class AutorForm extends BaseAutorForm{publicfunction configure(){

Page 71: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 71/167

 

$this->validatorSchema['email'] = new sfValidatorAnd(array($this->validatorSchema['email'],new sfValidatorEmail(),));}}

4.5.3. Modificando el widget 

El campo estado de la tabla articulo se define en el esquema como una cadena de

caracteres. Sus posibles valores se definen en la clase ArticuloPeer, como se muestra en

el listado 4-16.

Listado 4-16 - Definiendo los posibles valores del estado en la clase  ArticuloPeer 

class ArticuloPeer extends BaseArticuloPeer{static protected $estados = array('borrador', 'online', 'offline');

staticpublicfunction getEstados(){return self::$estados;}

// ...}

Cuando se modifica un artículo, el campo estado se debe representar como una lista

desplegable en vez de como un cuadro de texto. Para ello, se modifica el widget de la formaque se muestra en el listado 4-17.

Listado 4-17 - Modificando el widget del campo estado 

class ArticuloForm extends BaseArticuloForm{publicfunction configure(){$this->widgetSchema['estado'] = new sfWidgetFormSelect(array('choices' =>ArticuloPeer::getEstados()));}}

Por último, también es necesario modificar el validador para asegurar que el estado

seleccionado se encuentre dentro de la lista de estados posibles (ver listado 4-18).

Listado 4-18 - Modificando el validador del campo estado 

class ArticuloForm extends BaseArticuloForm{publicfunction configure(){$estados = ArticuloPeer::getEstados();

Page 72: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 72/167

 

 $this->widgetSchema['estado'] = new sfWidgetFormSelect(array('choices'=>$estados));

$this->validatorSchema['estado'] = new sfValidatorChoice(array('choices'=>array_keys($estados)));}}

4.5.4. Eliminando un campo

La tabla articulo tiene dos columnas especiales llamadas created_at y updated_at,

cuyos valores gestiona Propel de forma automática. Por lo tanto, para evitar que el usuariomodifique sus valores, es necesario eliminar estos dos campos del formulario, tal y como

muestra el listado 4-19.

Listado 4-19 - Eliminando un campo 

class ArticuloForm extends BaseArticuloForm{publicfunction configure(){unset($this->validatorSchema['created_at']);unset($this->widgetSchema['created_at']);

unset($this->validatorSchema['updated_at']);unset($this->widgetSchema['updated_at']);}}

Eliminar un campo consiste en eliminar su validador y su widget. El listado 4-20 muestra

cómo borrar los dos de forma simultánea mediante una sola instrucción, accediendo alformulario como si fuera un array de PHP.

Listado 4-20 - Accediendo al formulario como si fuera un array para eliminar un

campo 

class ArticuloForm extends BaseArticuloForm{publicfunction configure(){unset($this['created_at'], $this['updated_at']);}

}

4.5.5. Resultado final

Los listados 4-21 y 4-22 muestran respectivamente las versiones finales de los formularios

ArticuloForm y AutorForm después de su personalización.

Listado 4-21 - Formulario  ArticuloForm  

Page 73: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 73/167

 

class ArticuloForm extends BaseArticuloForm{publicfunction configure(){$autorCriteria = AutorPeer::getCriteriaAutoresActivos();

// widgets$this->widgetSchema['contenido']->setAttributes(array('rows' =>10, 'cols'=>40));$this->widgetSchema['estado'] = new sfWidgetFormSelect(array('choices' =>ArticuloPeer::getEstados()));$this->widgetSchema['autor_id']->setOption('criteria', $autorCriteria);

// validators$this->validatorSchema['slug']->setOption('required', false);$this->validatorSchema['contenido']->setOption('min_length', 5);$this->validatorSchema['estado'] = new sfValidatorChoice(array('choices'=>array_keys(ArticuloPeer::getEstados())));$this->validatorSchema['autor_id']->setOption('criteria',$autorCriteria);

unset($this['created_at']);unset($this['updated_at']);}}

Listado 4-22 - Formulario  AutorForm  

class AutorForm extends BaseAutorForm{publicfunction configure(){$this->validatorSchema['email'] = new sfValidatorAnd(array(

$this->validatorSchema['email'],new sfValidatorEmail(),));}}

La tarea propel:build-forms genera de forma automática la mayoría de elementos de los

formularios a partir de la información del modelo de datos. Este proceso automático es muyútil porque:

y  Simplifica el trabajo del programador, evitándole las tareas más tediosas y repetitivas. De

esta forma, el programador se centra en la configuración de los widgets y validadores para

que sigan la lógica de la aplicación.

y  Además, cada vez que se actualiza el esquema de la base de datos, también se actualizan

los formularios generados de forma automática. El único trabajo del programador consiste

en ajustar la configuración realizada.

La siguiente sección describe la personalización y configuración de las acciones y plantillas

generadas con la tarea propel:generate-crud.

Page 74: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 74/167

 

4.6. Serializando formularios

La sección anterior explica cómo personalizar los formularios generados automáticamente

con la tarea propel:build-forms. En esta sección se personaliza el flujo de trabajo del

formulario, empezando por el código generado mediante la tarea propel:generate-crud.

4.6.1. Valores por defecto

Una instancia de un formulario Propel siempre está asociada a un objeto Propel. Este objeto

asociado siempre es de la clase devuelta por el método getModelName(). El formulario

AutorForm por ejemplo sólo puede estar asociado a objetos que sean de la clase Autor.

Este objeto puede ser un objeto vacío (una nueva instancia de la clase Autor) o el objetoque se indica como primer argumento del constructor. A diferencia del constructor de un

formulario normal , al que se le pasa como primer argumento un array de valores, alconstructor de un formulario Propel se le pasa un objeto Propel. Este objeto se utiliza para

determinar el valor por defecto de cada campo del formulario. El método getObject() 

devuelve el objeto relacionado con la actual instancia del formulario y el método isNew()  permite descubrir si el objeto se ha obtenido a través del constructor:

// Crear un nuevo objeto$autorForm = new AutorForm();

print$autorForm->getObject()->getId(); // Muestra nullprint$autorForm->isNew(); // Muestra true

// Modificar un objeto existente$autor = AutorPeer::retrieveByPk(1);$autorForm = new AutorForm($autor);

print$autorForm->getObject()->getId(); // Muestra 1print$autorForm->isNew(); // Muestra false

4.6.2. Modificar el flujo de trabajo

Como se explica al principio de este capítulo, la acción edit mostrada en el listado 4-23gestiona el flujo de trabajo del formulario.

Listado 4-23 - El método executeEdit del módulo autor 

// apps/frontend/modules/autor/actions/actions.class.php

class autorActions extends sfActions{// ...

publicfunction executeEdit($request){$autor = AutorPeer::retrieveByPk($request->getParameter('id'));$this->form = new AutorForm($autor);

if($request->isMethod('post'))

Page 75: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 75/167

 

{$this->form->bind($request->getParameter('autor'));if($this->form->isValid()){$autor = $this->form->save();

$this->redirect('autor/edit?id='.$autor->getId());}}}}

Aunque la acción edit se parece a las acciones mostradas en los capítulos anteriores, en

realidad presenta diferencias importantes:

y  El primer argumento del constructor del formulario es un objeto Propel de la claseAutor: 

$autor = AutorPeer::retrieveByPk($request->getParameter('id'));$this->form = new AutorForm($autor);

y  El formato del atributo name de los widgets se modifica automáticamente para que sea

posible obtener la información del usuario en un array PHP que se llama igual que la tabla

relacionada (autor en este caso): 

$this->form->bind($request->getParameter('autor'));

y  Si el formulario es válido, sólo es necesario ejecutar el método save() para crear o

actualizar el objeto Propel relacionado con el formulario: 

$autor = $this->form->save();

4.6.3. Creando y modificando objetos Propel

El listado 4-23 utiliza un solo método para crear y modificar objetos de la clase Autor:

y  Crear un nuevo objeto de tipo Autor: 

o  Se ejecuta la acción index sin un parámetro id ($request-

>getParameter('id') es null)

o  La llamada al método retrieveByPk() devuelve null 

o  El objeto form se asocia con un objeto Propel vacío de tipo Autor.

o  La instrucción $this->form->save() crea por tanto un nuevo objeto de tipo

Autor siempre que los datos del formulario sean válidos.

y  Modificar un objeto de tipo Autor: 

o  Se ejecuta la acción index con un parámetro id obtenido mediante la instrucción

$request->getParameter('id') y que representa la clave primaria del

objeto Autor que se va a modificar.

o  La llamada al método retriveByPk() devuelve el objeto de tipoAutor 

relacionado con la clave primaria indicada.

Page 76: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 76/167

 

o  El objeto form se asocia al objeto de tipo Autor que se ha obtenido de la base de

datos.

o  La instrucción $this->form->save() actualiza los datos del objeto Autor 

siempre que los datos del formulario sean válidos.

4.6.4. El método save()

Cuando los datos de un formulario Propel son válidos, el método save() actualiza el objetoasociado y lo almacena en la base de datos. Este método no sólo guarda el objeto principal,

sino que también guarda todos los objetos relacionados. El formulario ArticuloForm por ejemplo actualiza las etiquetas relacionadas con el artículo. La relación entre las tablas

articulo y etiqueta es de tipo n-n y las etiquetas relacionadas con un artículo se guardan

en la tabla articulo_etiqueta utilizando el método saveArticuloEtiquetaList().

Para asegurar que los datos guardados son consistentes, el método save() realiza todas lasactualizaciones en una transacción.

Nota 

En el capítulo 9 se explica que el método save() también actualiza de forma automática

todas las tablas internacionalizadas.

Empleando el método bindAndSave()

El método bindAndSave() asocia al formulario los datos enviados por el usuario, valida elformulario y actualiza todos los objetos relacionados, todo ello en una única operación:

class articuloActions extends sfActions{publicfunction executeCreate(sfWebRequest $request){$this->form = new ArticuloForm();

if($request->isMethod('post')&&$this->form->bindAndSave($request->getParameter('articulo'))){$this->redirect('articulo/created');}}}

4.6.5. Trabajando con archivos subidos

El método save() actualiza de forma automática los objetos Propel, pero no puedeencargarse de otras cosas importantes como la gestión de los archivos subidos.

A continuación se muestra cómo adjuntar un archivo a cada artículo. Los archivos se

almacenan en el directorio web/uploads/ y en el campo archivo de la tabla articulo de

la base de datos se guarda la ruta hasta el archivo, tal y como muestra el listado 4-24.

Page 77: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 77/167

 

Listado 4-24 - Esquema de la tabla articulo con archivos adjuntos 

// config/schema.ymlpropel:articulo:// ...

archivo: varchar(255)

Después de actualizar el esquema, es necesario actualizar el modelo de objetos, la base de

datos y todos los formularios relacionados:

$ ./symfony propel:build-all

Cuidado 

Ten en cuenta que la tarea propel:build-all borra todas las tablas del esquema y lasvuelve a crear. Por lo tanto, se borran todos los datos de todas las tablas. Este es el

motivo por el que se crean archivos con datos de prueba (llamados  f  i xtures) y que se

 pueden cargar en la base de datos cada vez que se modifica el modelo.

El listado 4-25 muestra cómo modificar la clase ArticuloForm para asociar un widget y un

validador al campo archivo.

Listado 4-25 - Modificando el campo archivo del formulario  ArticuloForm  

class ArticuloForm extends BaseArticuloForm{publicfunction configure(){// ...

$this->widgetSchema['archivo'] = new sfWidgetFormInputFile();$this->validatorSchema['archivo'] = new sfValidatorFile();}}

Como sucede en todos los formularios que permite subir archivos, no olvides añadir el

atributo enctype a la etiqueta <form> de la plantilla (en el capítulo 2 se explica

detalladamente el trabajo con los archivos subidos).

El listado 4-26 muestra las modificaciones necesarias en la acción que guarda los datos del

formulario para guardar el archivo en el servidor y para guardar la ruta hasta el archivo en

el objeto articulo.

Listado 4-26 - Guardando el objeto articulo y el archivo subido 

publicfunction executeEdit($request){$autor = ArticuloPeer::retrieveByPk($request->getParameter('id'));$this->form = new ArticuloForm($autor);

Page 78: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 78/167

 

if($request->isMethod('post')){$this->form->bind($request->getParameter('articulo'), $request->getFiles('articulo'));if($this->form->isValid()){$archivo = $this->form->getValue('archivo');$nombreArchivo = sha1($archivo->getOriginalName()).$archivo->getExtension($archivo->getOriginalExtension());$archivo->save(sfConfig::get('sf_upload_dir').'/'.$nombreArchivo);

$articulo = $this->form->save();

$this->redirect('articulo/edit?id='.$articulo->getId());}}}

Después de guardar el archivo subido en el servidor, el objeto sfValidatedFile ya conoce

la ruta hasta el archivo. En el método save(), se utilizan los valores de cada campo paraactualizar el objeto asociado. Además, el objeto sfValidatedFile se convierte en una

cadena de texto utilizando el método __toString(), que devuelve la ruta completa hasta el

archivo. La columna archivo de la tabla articulo almacena esta ruta completa.

Sugerencia 

Si se quiere almacenar la ruta del archivo de forma relativa respecto al directorio

sfConfig::get('sf_upload_dir'), se puede crear una clase que herede de

sfValidatedFile y se puede utilizar la opción validated_file_class para indicar al

validador sfValidatorFile el nombre de la nueva clase. De esta forma, el validador 

devuelve una instancia de esa nueva clase. En lo que queda de capítulo se muestra otraforma de conseguirlo, que consiste en modificar el valor de la columna archivo antes de

guardar el objeto en la base de datos.

4.6.6. Personalizando el método save()

En la sección anterior se explica cómo guardar un archivo subido en la acción edit. Por 

otra parte, uno de los principios de la programación orientada a objetos es la reutilizacióndel código cuando se encapsula en clases. De esta forma, en vez de duplicar el código

necesario para guardar los archivos subidos en todas las acciones del formulario

ArticuloForm, se guarda ese código en la propia clase ArticuloForm. El listado 4-27

muestra cómo redefinir el método save() para que también guarde el archivo subido y paraque pueda borrar un archivo existente.

Listado 4-27 - Redefiniendo el método save() de la clase  ArticuloForm  

class ArticuloForm extends BaseFormPropel{// ...

Page 79: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 79/167

 

publicfunction save($con = null){if(file_exists($this->getObject()->getArchivo())){unlink($this->getObject()->getArchivo());}

$archivo = $this->getValue('archivo');$nombreArchivo = sha1($archivo->getOriginalName()).$archivo->getExtension($archivo->getOriginalExtension());$archivo->save(sfConfig::get('sf_upload_dir').'/'.$nombreArchivo);

return parent::save($con);}}

Después de incluir todo el código en el propio formulario, la nueva acción edit es idéntica

a la que genera automáticamente la tarea propel:generate-crud.

Refactorizando el código en el modelo del formulario

 Normalmente, las acciones generadas de forma automática por la tarea propel:generate-

crud no se modifican.

Todo el código que puede ser necesario añadir en la acción edit, especialmente el códigorelacionado con la serialización del formulario, debe ser incluido realmente en las clases delmodelo o en las clases del formulario.

En los ejemplos anteriores se muestra cómo refactorizar la clase del formulario para tener 

en cuenta los archivos subidos. A continuación se muestra otro ejemplo relacionado con el

modelo. El formulario ArticuloForm dispone de un campo llamado slug. Como seexplicó anteriormente, este campo se genera automáticamente a partir del valor del campo

titulo, que puede ser modificado por el usuario. Esta lógica no depende del formulariosino que realmente está relacionada con el modelo, como se muestra en el siguiente código:

class Articulo extends BaseArticulo{publicfunction save($con = null){if(!$this->getSlug()){$this->setSlugFromTitulo();

}

return parent::save($con);}

protection function setSlugFromTitulo(){// ...}}

Page 80: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 80/167

 

El objetivo final de estas refactorizaciones es la separación de la aplicación en diferentes

capas y la posibilidad de reutilizar trozos de código.

4.6.7. Personalizando el método doSave()

Como se ha comentado en las secciones anteriores, la actualización de los objetos se realizamediante transacciones que aseguran que todas las operaciones relacionadas con esa

actualización se realizan correctamente. Cuando se redefine el método save() (como por ejemplo en la sección anterior para guardar los archivos subidos) el código que se ejecutano se incluye en esta transacción.

El listado 4-28 muestra cómo utilizar el método doSave() para incluir en la transacciónglobal el código propio que guarda los archivos subidos.

Listado 4-28 - Redefiniendo el método doSave() en el formulario  ArticuloForm  

class ArticuloForm extends BaseFormPropel{// ...

publicfunction doSave($con = null){if(file_exists($this->getObject()->getArchivo())){unlink($this->getObject()->getArchivo());}

$archivo = $this->getValue('archivo');$nombreArchivo = sha1($archivo->getOriginalName()).$archivo->getExtension($archivo->getOriginalExtension());

$archivo->save(sfConfig::get('sf_upload_dir').'/'.$nombreArchivo);

return parent::doSave($con);}}

De esta forma, si el método save() del objeto archivo produce una excepción, el objeto

articulo tampoco se guarda porque el código propio se ha incluido dentro del método

doSave() que realiza la transacción.

4.6.8. Personalizando el método ''updateObject()''

En ocasiones es necesario modificar el objeto asociado al formulario entre la acción deactualizar sus datos y la acción de guardarlo en la base de datos.

En el ejemplo anterior de los archivos subidos, en vez de guardar en la columna archivo laruta completa hasta el archivo subido, sólo se guarda la ruta relativa respecto al directorio

sfConfig::get('sf_upload_dir').

Page 81: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 81/167

 

El listado 4-29 muestra cómo redefinir el método updateObject() del formulario

ArticuloForm para modificar el valor de la columna archivo después de actualizar 

automáticamente el objeto pero antes de guardarlo en la base de datos.

Listado 4-29 - Redefiniendo el método updateO bject() y la clase  ArticuloForm  

class ArticuloForm extends BaseFormPropel{// ...

publicfunction updateObject(){$objeto = parent::updateObject();

$objeto->setArchivo(str_replace(sfConfig::get('sf_upload_dir').'/', '',$objeto->getArchivo()));

return$objeto;}}

El método updateObject() se invoca desde el método doSave() antes de guardar elobjeto en la base de datos.

Capítulo 8. Internacionalización y localización

Muchas aplicaciones web populares está disponibles en varios idiomas e incluso algunas de

ellas modifican sus características en función de la cultura del usuario. Symfony incluyeutilidades para facilitar la gestión de estas características (como se explica en el capítulo 13del libro oficial de Symfony 1.1).

La parte de los formularios también incluye soporte para traducir la interfaz de usuario y permite gestionar de forma sencilla los objetos internacionalizados.

8.1. Internacionalización de formularios

Los formularios de Symfony son internacionalizables por defecto. La traducción de los

títulos o labels, los mensajes de ayuda y los mensajes de error se realiza mediante la

traducción de los ficheros XLIFF, gettext o cualquier otro formato soportado por 

Symfony.

El listado 8-1 muestra el formulario de contacto desarrollado en los capítulos anteriores.

Page 82: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 82/167

 

Listado 8-1 - Formularios de contacto 

class FormularioContacto extends sfForm{publicfunction configure(){

$this->setWidgets(array('nombre' =>new sfWidgetFormInput(), // el título o label por defectoes "Nombre"'email' =>new sfWidgetFormInput(), // el título o label por defectoes "Email"'mensaje' =>new sfWidgetFormTextarea(), // el título o label por defectoes "Mensaje"));

// Modificar el título del widget del campo email$this->widgetSchema->setLabel('email', 'Dirección de email');}}

El listado 8-2 muestra cómo traducir los títulos anteriores al francés utilizando un archivo

en formato XLIFF.

Listado 8-2 - Archivo de traducción en formato XLIFF 

// apps/frontend/i18n/messages.fr.xml<?xmlversion="1.0"?><xliffversion="1.0"><fileorginal="global"source-language="en"datatype="plaintext"><body><trans-unit><source>Name</source>

<target>Nom</target></trans-unit><trans-unit><source>Email address</source><target>Adresse email</target></trans-unit><trans-unit><source>Body</source><target>Message</target></trans-unit></body></file></xliff>

8.1.1. Indicar el catálogo utilizado para la traducción

Si utilizas la opción de los catálogos de la parte de internacionalización de Symfony,

 puedes asociar un formulario con un catálogo existente. El listado 8-3 asocia el fomulario

FormularioContacto con el catálogo formulario_contacto. Por lo tanto, las

traducciones de los elementos de ese formulario se buscarán en el archivo

formulario_contacto.fr.xml.

Page 83: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 83/167

 

Listado 8-3 - Estableciendo el catálogo utilizado para la traducción 

class FormularioContacto extends sfForm{publicfunction configure(){

// ...

$this->widgetSchema->getFormFormatter()->setTranslationCatalogue('formulario_contacto');}}

Nota 

Utilizar catálogos permite organizar mejor las traducciones, ya que por ejemplo se puedeutilizar un archivo para cada formulario.

8.1.2. Internacionalización de los mensajes de error

En ocasiones, los mensajes de error incluyen el valor enviado por el usuario, como por 

ejemplo: "La dirección de email usuario@dominio no es válida." . El el capítulo 2 seexplicó lo fácil que es hacerlo definiendo mensajes de error personalizados en la clase del

formulario y utilizando referencias a los valores enviados por el usuario. Las referencias

siguen el patron %nombre_parametro%.

El listado 8-4 muestra cómo aplicar este principio al campo nombre del formulario decontacto.

Listado 8-4 - Internacionalización de los mensajes de error 

class FormularioContacto extends sfForm{publicfunction configure(){// ...

$this->validatorSchema['nombre'] = new sfValidatorEmail(array('min_length' =>2, 'max_length' =>45),array('min_length' =>'El nombre "%value%" debe tener al menos%min_length% caracteres.','max_length' =>'El nombre "%value%" no puede tener más de %max_length%caracteres.',

),);}}

Para traducir los mensajes de error anteriores, sólo es necesario editar el archivo en formato

XLIFF como muestra el listado 8-5.

Listado 8-5 - Archivo de traducción en formato XLIFF para los mensajes de error 

Page 84: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 84/167

 

<trans-unit><source>El nombre "%value%" debe tener al menos %min_length%caracteres</source><target>Le nom "%value%" doit comporter un minimum de %min_length%caractères</target></trans-unit><trans-unit><source>El nombre "%value%" no puede tener más de %max_length%caracteres</source><target>Le nom "%value%" ne peut comporter plus de %max_length%caractères</target></trans-unit>

8.2. Personalizando el objeto traductor

Si quieres utilizar los formularios de Symfony sin la parte de internacionalización deSymfony, debes crear un objeto traductor propio. Un objeto traductor es simplemente un

elemento PHP que se puede ejecutar (un PHP callable), por lo que puede ser cualquiera deestos tres elementos:

y  Una cadena de texto que representa el nombre de una función, como por ejemplomi_funcion 

y  Un array con el nombre de la instancia de una clase (es decir, el nombre de un objeto) y el

nombre de uno de sus métodos, como por ejemploarray($unObjeto,'nombreDeUnoDeSusMetodos') 

y  Una instancia de sfCallable. Esta clase se utiliza para encapsular de forma consistente

un elemento ejecutable de PHP.

Nota 

Un elemento ejecutable de PHP (también llamado PHP callable) es una referencia a una

función o al método de un objeto. También puede ser una variable PHP que devuelve true 

cuando se pasa a la función is_callable().

En el siguiente ejemplo se considera que se está migrando un proyecto que ya dispone de su propio mecanismo de internacionalización a través de la clase que se muestra en el listado

8-6.

Listado 8-6 - Clase propia para la internacionalización 

class miI18n{static protected $cultura_defecto = 'en';static protected $mensajes = array('fr' =>array('Nombre' =>'Nom','Email' =>'Courrier électronique','Asunto' =>'Sujet','Mensaje' =>'Message',));

Page 85: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 85/167

 

 staticpublicfunction traducirTexto($texto){$cultura = isset($_SESSION['cultura']) ? $_SESSION['cultura'] :self::$cultura_defecto;if(array_key_exists($cultura, self::$mensajes)&&array_key_exists($texto, self::$mensajes[$cultura])){return self::$mensajes[$_SESSION['cultura']][$texto];}return$texto;}}

// Ejemplos de uso de la clase anterior$miI18n = new miI18n();

$_SESSION['cultura'] = 'es';echo$miI18n->translateText('Asunto'); // => muestra "Asunto"

$_SESSION['cultura'] = 'fr';echo$miI18n->translateText('Asunto'); // => muestra "Sujet"

Como se muestra en el listado 8-7, cada formulario puede definir el elemento PHP que seencarga de traducir los elementos del formulario.

Listado 8-7 - Redefiniendo el método de internacionalización de un formulario 

class FormularioContacto extends sfForm{publicfunction configure(){

// ...$this->widgetSchema->getFormFormatter()->setTranslationCallable(array(newmiI18n(), 'traducirTexto'));}}

8.2.1. Parámetros del objeto traductor

El objeto traductor puede recibir hasta tres parámetros:

y  El texto que debe traducir.

y  Un array asociativo con los elementos que se deben sustituir en el texto original,

normalmente utilizados para reemplazar argumentos dinámicos como se ha vistopreviamente en este capítulo.

y  El nombre del catálogo que se debe utilizar para traducir el texto.

A continuación se muestra la llamada que realiza el método

sfFormWidgetSchemaFormatter::translate() para ejecutar el objeto traductor:

returncall_user_func(self::$translationCallable, $texto, $parametros,$catalogo);

Page 86: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 86/167

 

El elemento self::$translationCallable es una referencia al objeto traductor. Por lotanto, el código anterior es equivalente al siguiente:

$miI18n->traducirTexto($texto, $parametros, $catalogo);

Seguidamente se muestra la versión actualizada de la clase MiI18n que soporta estos

argumentos adicionales:

class miI18n{static protected $cultura_defecto = 'en';static protected $mensajes = array('fr' =>array('mensajes' =>array('Nombre' =>'Nom','Email' =>'Courrier électronique','Asunto' =>'Sujet','Mensaje' =>'Message',),));

staticpublicfunction traducirTexto($texto, $parametros = array(),$catalogo = 'mensajes'){$cultura = isset($_SESSION['cultura']) ? $_SESSION['cultura'] :self::$cultura_defecto;if(array_key_exists($cultura, self::$mensajes)&&array_key_exists($mensajes, self::$mensajes[$cultura]&&array_key_exists($texto, self::$mensajes[$cultura][$mensajes])){$texto = self::$mensajes[$_SESSION['cultura']][$mensajes][$texto];$texto = strtr($texto, $parametros);}

return$texto;}}

¿Por qué se utiliza sfWidgetFormSchemaFormatter para personalizar el mecanismo de

traducción?

Como se muestra en el capítulo 2, el mecanismo de formularios sigue la arquitectura MVC

y la clase sfWidgetFormSchemaFormatter corresponde a la capa de la vista. Esta clase se

encarga de mostrar todo el texto, por lo que tiene acceso a todas las cadenas de texto y puede traducirlas de forma dinámica.

8.3. Internacionalización de los objetos Propel

Los formularios de Symfony incluyen el soporte de los objetos Propel internacionalizados.

Para mostrar su funcionamiento se utiliza el siguiente modelo de datos de ejemplo consoporte de internacionalización:

propel:

Page 87: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 87/167

 

articulo:id:autor: varchar(255)

created_at:articulo_i18n:titulo: varchar(255)

contenido: longvarchar

Utilizando los siguientes comandos se pueden generar las clases Propel y las clases delformulario:

$ php symfony build:model$ php symfony build:forms

Los comandos anteriores generan varios archivos en el proyecto de Symfony:

lib/ form/ ArticuloForm.class.phpArticuloI18nForm.class.phpBaseFormPropel.class.php

model/ Articulo.phpArticuloPeer.phpArticuloI18n.phpArticuloI18nPeer.php

El listado 8-8 muestra cómo configurar el formulario ArticuloForm para poder editar enun mismo formulario las versiones en inglés y francés de un artículo.

Listado 8-8 - Formularios I18n de un objeto Propel internacionalizado 

class ArticuloForm extends BaseArticuloForm{publicfunction configure(){$this->embedI18n(array('en', 'fr'));}}

También es posible personalizar el nombre de los idiomas del formulario añadiendo en el

método configure() el código mostrado en el listado 8-9.

Listado 8-9 - Personalizando el nombre de los idiomas 

$this->widgetSchema->setLabel('en', 'Inglés');$this->widgetSchema->setLabel('fr', 'Francés');

Figura 8-1 - Formulario Propel internacionalizado 

Page 88: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 88/167

 

 

Figura 8.1. Formulario Propel internacionalizado

Y eso es todo. Cuando se ejecuta el método save() del objeto del formulario, se guardande forma automática el objeto Propel asociado y todos los objetos de tipo i18n.

¿Cómo se pasa la cultura del usuario al formulario?

Si se quiere asociar un formulario con la cultura del usuario, se puede utilizar una opción

llamada culture cuando se crea el formulario:

class articuloActions extends sfActions{publicfunction executeCrear($peticion){$this->form = new ArticuloForm(null, array('culture' =>$this->getUser()->getCulture()));if($peticion->isMethod('post')&&$this->form->bindAndSave($peticion->getParameter('articulo'))){$this->redirect('articulo/creado');}}}

En la clase ArticuloForm se puede obtener el valor de la cultura mediante el array

options:

class ArticuloForm extends BaseArticuloForm{publicfunction configure(){$this->embedI18n(array($this->getCurrentCulture()));}publicfunction getCurrentCulture(){

Page 89: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 89/167

 

returnisset($this->options['culture']) ? $this->options['culture'] :'en';}}

8.4. Traducción de Widgets

Los formularios de Symfony incluyen algunos widgets preparados para su

internacionalización. De esta forma se pueden adaptar a la cultura establecida por elusuario.

8.4.1. Selectores de fechas

Los widgets disponibles para traducir y adaptar las fechas son los siguientes:

y El widget sfWidgetFormI18nDate permite introducir fechas (día, mes y año)

:

 

$this->widgetSchema['publicado_en'] = newsfWidgetFormI18nDate(array('culture' =>'fr'));

y  También se puede modificar el formato del mes, gracias a la opción month_format que

permite elegir entre tres formatos: 

o  name, muestra el nombre del mes (este es el formato por defecto)

o  short_name, muestra la abreviatura del nombre del mes

o  number, muestra el número del mes (desde el1 hasta el 12)

y  El widget sfWidgetFormI18nTime permite introducir valores de tiempo (horas, minutos

y segundos): 

$this->widgetSchema['publicado_en'] = newsfWidgetFormI18nTime(array('culture' =>'fr'));

y  El widget sfWidgetFormI18nDateTime permite introducir fechas y horas: 

$this->widgetSchema['publicado_en'] = newsfWidgetFormI18nDateTime(array('culture' =>'fr'));

8.4.2. Selector de país

El widget sfWidgetFormI18nSelectCountry muestra una lista desplegable con el nombrede todos los países. El nombre de los países se muestra en el idioma correspondiente a la

cultura del usuario:

$this->widgetSchema['pais'] = newsfWidgetFormI18nSelectCountry(array('culture' =>'fr'));

Page 90: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 90/167

 

Si no se quiere mostrar la lista completa de todos los países del mundo, se pueden restringir 

sus elementos con la opción countries:

$paises = array('fr', 'en', 'es', 'de', 'nl');$this->widgetSchema['pais'] = newsfWidgetFormI18nSelectCountry(array('culture' =>'fr',

'countries' =>$paises));

8.4.3. Selector de idioma

El widget sfWidgetFormI18nSelectLanguage muestra una lista desplegable con el

nombre de varios idiomas del mundo. El nombre de cada idioma se muestra en el idioma

correspondiente a la cultura del usuario:

$this->widgetSchema['idioma'] = newsfWidgetFormI18nSelectLanguage(array('culture' =>'fr'));

Si no se quiere mostrar la lista completa de todos los idiomas del mundo, se pueden

restringir sus elementos con la opción languages:

$idiomas = array('fr', 'en', 'es', 'de', 'nl');$this->widgetSchema['idioma'] = newsfWidgetFormI18nSelectLanguage(array('culture' =>'fr','languages' =>$idiomas));

Capítulo 11. Integrando Doctrine

En los proyectos web, la mayoría de formularios se utilizan para crear y modificar los

objetos del modelo. Normalmente, el contenido de los objetos se guarda en una base dedatos mediante un ORM. Los formularios de Symfony incluyen una capa adicional para

interactuar con Doctrine, uno de los dos ORM incluidos en Symfony, de forma que sesimplifica la creación de formularios que utilizan estos objetos del modelo.

En este capítulo se detalla cómo integrar los formularios de Symfony con los modelos queutilizan objetos de Doctrine. Por este motivo, para leer este capítulo es imprescindible

disponer de conocimientos previos de Doctrine y de su integración con Symfony. Si no estu caso, puedes leer el capítulo 8 (El Modelo) del libro oficial de Symfony 1.1.

11.1. Antes de comenzarA lo largo de este capítulo se crea un gestor de artículos como ejemplo de uso de Doctrine ylos formularios de Symfony. En primer lugar se define el esquema de la base de datos,

compuesto por las cinco tablas mostradas en el listado 11-1: article, author, category,

tag y article_tag.

Listado 11-1 - Esquema de la base de datos 

Page 91: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 91/167

 

// config/doctrine/schema.ymlArticle:actAs: [Sluggable, Timestampable]columns:title:type: string(255)notnull: true

content:type: clob

status: string(255)author_id: integercategory_id: integerpublished_at: timestamp

relations:Author:foreignAlias: Articles

Category:foreignAlias: Articles

Tags:class: Tag

refClass: ArticleTagforeignAlias: Articles

Author:columns:first_name: string(20)last_name: string(20)email: string(255)

active: booleanCategory:columns:name: string(255)

Tag:columns:name: string(255)

ArticleTag:columns:article_id:type: integerprimary: true

tag_id:type: integerprimary: true

relations:Article:onDelete: CASCADE

Tag:onDelete: CASCADE

A continuación se muestra la relación entre las tablas:

y  Relación 1-n entre las tablas article y author, ya que cada artículo está escrito por uno y sólo un autor.

y  Relación 1-n entre las tablas article y category, ya que cada artículo pertenece a

una o ninguna categoría.

y  Relación n-n entre las tablas article y tag.

Page 92: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 92/167

 

11.2. Generando las clases del formulario

El gestor de artículos permite modificar la información de las tablas article, author,

category y tag. Para ello, es preciso crear formularios asociados a cada tabla y configurar los widgets y validadores necesarios para cumplir las restricciones del esquema de la base

de datos. Aunque los formularios se pueden crear a mano, se trata de una tarea repetitiva ytediosa que requiere repetir la misma información una y otra vez en diferentes archivos (el

nombre de las columnas y los campos, el tamaño máximo de las columnas y campos, etc.)Además, cada vez que se modifica el modelo, es necesario modificar la clase del formulario

asociado. Afortunadamente, el plugin de Doctrine incluye una tarea llamada

doctrine:build-forms que automatiza todo este proceso y genera los formulariosrelacionados con el modelo de objetos:

$ ./symfony doctrine:build-forms frontend

La tarea de generación automática de formularios crea una clase para cada tabla y añade los

validadores y widgets necesarios para cada columna mediante la introspección del modeloy teniendo en cuenta las relaciones entre tablas.

Nota 

Las tareas doctrine:build-all y doctrine:build-all-load también actualizan las

clases de los formularios porque invocan automáticamente la tarea doctrine:build-

forms.

Después de ejecutar estas tareas se crea una estructura de archivos en el directorio

lib/form/. El siguiente listado muestra los archivos creados para el esquema utilizado en

los ejemplos anteriores:

lib/ form/ doctrine/ BaseFormDoctrine.class.phpArticleForm.class.phpArticleTagForm.class.phpAuthorForm.class.phpCategoryForm.class.phpTagForm.class.phpbase/ BaseArticleForm.class.php

BaseArticleTagForm.class.phpBaseAuthorForm.class.phpBaseCategoryForm.class.phpBaseFormDoctrine.class.phpBaseTagForm.class.php

La tarea doctrine:build-forms genera dos clases para cada tabla del esquema, una clase

 base en el directorio lib/form/base y otra clase en el directorio lib/form/. Si se

considera por ejemplo la tabla author, se crean las clases BaseAuthorForm y AuthorForm 

Page 93: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 93/167

 

que se guardan en los archivos lib/form/base/BaseAuthorForm.class.php y

lib/form/AuthorForm.class.php.

La siguiente tabla muestra la jerarquía de las diferentes clases relacionadas con el

formulario AuthorForm.

ClasePaquete

( package)

Quién utiliza

la claseDescripción

AuthorForm  project  Programador Redefine el formulario generado

BaseAuthorForm  project  SymfonySe basa en el esquema y se genera cadavez que se ejecuta la tareadoctrine:build-forms 

BaseFormDoctrine project  Programador Permite personalizar los formularios de

Doctrine de forma global

sfFormDoctrine PluginDoctrine

Symfony Base de los formularios de Doctrine

sfForm  symfony  Symfony Base de los formularios de Symfony

El listado 11-2 muestra el código de la clase AuthorForm, utilizada para crear y modificar 

los objetos de la clase Author. Como se puede observar, esta clase no contiene ningún

método, ya que hereda de la clase BaseAuthorForm que se genera a partir de la

configuración. La clase AuthorForm se utiliza para personalizar y redefinir la configuracióndel formulario.

Listado 11-2 - La clase  AuthorForm  

class AuthorForm extends BaseAuthorForm{public function configure(){}

}

El listado 11-3 muestra el código de la clase BaseAuthorForm con los validadores y

widgets generados automáticamente mediante la instrospección del modelo de la tabla

author.

Listado 11-3 - Clase Base AuthorForm que representa al formulario de la tabla author 

class BaseAuthorForm extends BaseFormDoctrine{public function setup(){$this->setWidgets(array('id' => new sfWidgetFormInputHidden(),'first_name' => new sfWidgetFormInput(),'last_name' => new sfWidgetFormInput(),'email' => new sfWidgetFormInput(),

Page 94: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 94/167

 

));

$this->setValidators(array('id' => new sfValidatorDoctrineChoice(array('model' =>

'Author', 'column' => 'id', 'required' => false)),'first_name' => new sfValidatorString(array('max_length' => 20,

'required' => false)),'last_name' => new sfValidatorString(array('max_length' => 20,

'required' => false)),'email' => new sfValidatorString(array('max_length' => 255)),

));

$this->widgetSchema->setNameFormat('author[%s]');

$this->errorSchema = new sfValidatorErrorSchema($this->validatorSchema);

parent::setup();}

public function getModelName(){

return 'Author';}

}

La clase generada es muy similar a la de los formularios creados en los capítulos anteriores,

salvo por las siguientes excepciones:

y  La clase base es BaseFormDoctrine en vez de sfForm 

y  La configuración de los validadores y widgets se realiza en el método setup(), en

vez de en el método configure() 

y  El método getModelName() devuelve la clase de Doctrine relacionada con elformulario

Personalizando de forma global los formularios de Doctrine

Además de las clases generadas para cada tabla, la tarea doctrine:build-forms también

genera la clase BaseFormDoctrine. Esta clase está vacía inicialmente y es la clase base de

todas las clases del directorio lib/form/base/, por lo que permite modificar globalmenteel comportamiento de todos los formularios de Doctrine. El siguiente ejemplo muestracómo modificar el formato de salida de todos los formularios de Doctrine:

abstract class BaseFormDoctrine extends sfFormDoctrine{public function setup(){sfWidgetFormSchema::setDefaultFormFormatterName('div');

}}

Page 95: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 95/167

 

Si te fijas verás que la clase BaseFormDoctrine hereda a su vez de la clase

sfFormDoctrine. Esta última clase incluye funcionalidades específicas de Doctrine y entre

otras cosas, incluye todo el código necesario para almacenar en la base de datos los objetoscon los valores enviados a través de los formularios.

Sugerencia 

Las clases base utilizan el método setup() para su configuración en vez del método

configure(). De esta forma es posible redefinir la configuración de las clases vacías

generadas automáticamente sin utilizar una llamada a parent::configure().

Los nombres de los campos del formulario son idénticos a los nombres de las columnas del

esquema de datos: id, first_name, last_name y email.

La tarea doctrine:build-forms genera un widget y un validador para cada columna de la

tabla author cumpliendo las restricciones del esquema de datos. Esta tarea siempre genera

los validadores más seguros posible. Si se considera el campo id, se podría comprobar simplemente si es un número entero válido. Sin embargo, el validador generado también

comprueba que ese identificador exista en la base de datos (para modificar un objetoexistente) o sea vacío (para poder crear un nuevo objeto). La validación generada

automáticamente es mucho más segura que comprobar simplemente que sea un númeroentero.

La mayor ventaja de los formularios generados es que se pueden utilizar inmediatamente.

Si se añade la instrucción <?php echo $form ?> en la página, ya se dispone de unformulario completamente funcional sin haber escrito ni una sola línea de código.

Además de la posibilidad de crear prototipos rápidamente, los formularios generados se pueden modificar sin necesidad de cambiar las clases generadas automáticamente, gracias a

la herencia entre las clases base y las clases de los formularios.

Por último, cada vez que se modifica el esquema de la base de datos, esta tarea genera de

nuevo todos los formularios para tener en cuenta las modificaciones manteniendo lasmodificaciones realizadas en las otras clases.

11.3. El generador CRUD

Una vez generadas las clases de los formularios, a continuación se crea un módulo deSymfony que permita trabajar con los objetos a través de un navegador. El objetivo es

crear, modificar y borrar objetos de las clases Article, Author, Category y Tag.

En primer lugar se crea el módulo correspondiente a la clase Author. Aunque este módulo

se puede crear manualmente, el plugin de Doctrine incluye la tarea doctrine:generate-

crud para crear un módulo de tipo CRUD basado en la clase de un objeto del modelo. Si seemplea el mismo formulario que en la sección anterior:

Page 96: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 96/167

 

$ ./symfony doctrine:generate-crud frontend author Author

La tarea doctrine:generate-crud requiere tres argumentos:

y  frontend: nombre de la aplicación en la que se crea el módulo

y author: nombre del módulo creado

y  Author: nombre de la clase del modelo para la que se crea el módulo

Nota 

CRUD es el acrónimo de las palabras inglesas "C reation / Retrieval / Update / Deletion"  

(Crear, O btener, Actualizar y Borrar) que resumen las cuatro operaciones básicas que serealizan con los datos del modelo.

El listado 11-4 muestra que la tarea genera cinco acciones que permiten listar (index),

crear (create), modificar (edit), guardar (update) y borrar (delete) los objetos de la

clase Author.

Listado 11-4 - La clase authorActions generada por la tarea 

// apps/frontend/modules/author/actions/actions.class.phpclass authorActions extends sfActions{public function executeIndex(){$this->authorList = $this->getAuthorTable()->findAll();

}

public function executeCreate()

{$this->form = new AuthorForm();

$this->setTemplate('edit');}

public function executeEdit($request){$this->form = $this->getAuthorForm($request->getParameter('id'));

}

public function executeUpdate($request){$this->forward404Unless($request->isMethod('post'));

$this->form = $this->getAuthorForm($request->getParameter('id'));

$this->form->bind($request->getParameter('author'));if ($this->form->isValid()){$author = $this->form->save();

$this->redirect('author/edit?id='.$author->get('id'));

Page 97: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 97/167

 

}

$this->setTemplate('edit');}

public function executeDelete($request){$this->forward404Unless($author = $this->getAuthorById($request-

>getParameter('id')));

$author->delete();

$this->redirect('author/index');}

private function getAuthorTable(){return Doctrine::getTable('Author');

}

private function getAuthorById($id){return $this->getAuthorTable()->find($id);

}

private function getAuthorForm($id){$author = $this->getAuthorById($id);

if ($author instanceof Author){return new ArticleForm($author);

}else{return new ArticleForm();

}}

}

El flujo de trabajo del formulario de este módulo se controla mediante los métodos create,

edit y update. Por tanto, la tarea doctrine:generate-crud también puede generar un

único método que se encargue de estas funcionalidades mediante la opción --non-atomic-

actions:

$ ./symfony doctrine:generate-crud frontend author Author --non-atomic-actions

El código generado con la opción --non-atomic-actions (ver listado 11-5) es mucho másconciso.

Listado 11-5 - La clase authorActions generada con la opción --non-atomic-actions 

class authorActions extends sfActions

Page 98: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 98/167

 

{public function executeIndex(){$this->authorList = $this->getAuthorTable()->findAll();

}

public function executeEdit($request){$this->form = new AuthorForm(Doctrine::getTable('Author')-

>find($request->getParameter('id')));

if ($request->isMethod('post')){$this->form->bind($request->getParameter('author'));if ($this->form->isValid()){$author = $this->form->save();

$this->redirect('author/edit?id='.$author->getId());}

}}

public function executeDelete($request){$this->forward404Unless($author = Doctrine::getTable('Author')-

>find($request->getParameter('id')));

$author->delete();

$this->redirect('author/index');}}

Esta tarea también genera dos plantillas, indexSuccess y editSuccess. La plantilla

editSuccess generada no utiliza la instrucción <?php echo $form ?>. Se puede

modificar este comportamiento con la opción --non-verbose-templates:

$ ./symfony doctrine:generate-crud frontend author Author --non-verbose-templates

Esta última opción es muy útil cuando se están creando los prototipos, tal y como muestra

el listado 11-6.

Listado 11-6 - La plantilla editSuccess 

// apps/frontend/modules/author/templates/editSuccess.php<?php $author = $form->getObject() ?><h1><?php echo $author->isNew() ? 'New' : 'Edit' ?> Author</h1>

<form action="<?php echo url_for('author/edit'.(!$author->isNew() ?'?id='.$author->getId() : '')) ?>" method="post" <?php $form->isMultipart() and print 'enctype="multipart/form-data" ' ?>><table>

Page 99: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 99/167

 

<tfoot><tr><td colspan="2">&nbsp;<a href="<?php echo url_for('author/index') ?>">Cancel</a><?php if (!$author->isNew()): ?>&nbsp;<?php echo link_to('Delete', 'author/delete?id='.$author->getId(),array('post' => true, 'confirm' => 'Are you sure?')) ?><?php endif; ?><input type="submit" value="Save" /></td></tr></tfoot><tbody><?php echo $form ?></tbody></table></form>

Sugerencia 

La opción --with-show permite generar una acción y una plantilla específicas paravisualizar los datos de un objeto. Esta plantilla solamente permite visualizar los datos, nomodificarlos.

Si accedes ahora a la dirección /frontend_dev.php/author con un navegador, puedesvisualizar el módulo generado automáticamente (ver figuras 11-1 y 11-2). Prueba a listar 

los autores, crear nuevos autores, modificar sus datos e incluso borrarlos gracias a lainterfaz generada. Como puedes comprobar, las reglas de validación también se tienen en

cuenta al crear o modificar los datos.

Figura 11.1. Listado de autores

Page 100: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 100/167

 

 

Figura 11.2. Errores de validación al modificar los datos de un autor 

Si ahora se repite la operación con la clase Article:

$ ./symfony doctrine:generate-crud frontend article Article --non-verbose-templates --non-atomic-actions

El código generado es muy parecido al código de la clase Author. No obstante, si intentascrear un nuevo artículo, la aplicación produce el error fatal que se muestra en la figura 11-3.

Figura 11.3. Las tablas relacionadas deben definir el método __toString()

El formulario ArticleForm utiliza el widget sfWidgetFormDoctrineSelect para

representar la relación entre los objetos Article y Author. Este widget crea una listadesplegable con todos los autores disponibles. Cuando la aplicación trata de mostrar los

autores, los objetos de tipo Author se convierten en una cadena de texto gracias al método

mágico __toString(). Por lo tanto, es obligatorio definir el método __toString() en laclase Author tal y como muestra el listado 11-7.

Listado 11-7 - Definiendo el método  __toString() en la clase  Author 

class Author extends BaseAuthor{public function __toString()

Page 101: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 101/167

 

{return $this->getFirstName().' '.$this->getLastName();

}}

De la misma forma que en la clase Author, se pueden crear métodos __toString() en las

otras clases del modelo: Article, Category y Tag.

Nota 

sfDoctrineRecord intenta adivinar cuál es el método __toString() adecuado si no lo

encuentra. Para ello comprueba si existen columnas llamadas title, name, subject, etc. Siexiste alguna de esas columnas, la utiliza como representación del objeto en forma de

cadena de texto.

Sugerencia 

La opción method del widget sfWidgetFormDoctrineSelect establece el métodoutilizado para obtener la representación del objeto en forma de cadena de texto.

La figura 11-4 muestra cómo crear un artículo después de haber incluído el método

__toString().

Page 102: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 102/167

 

Figura 11.4. Creando un artículo

11.4. Personalizando los formularios generados

Las tareas doctrine:build-forms y doctrine:generate-crud permiten crear módulos

de Symfony totalmente funcionales para listar, crear, editar y borrar objetos del modelo dedatos. Estos módulos tienen en cuenta las reglas de validación y las relaciones entre tablas.

Además, todo esto se consigue sin escribir ni una sola línea de código.

El siguiente paso consiste en personalizar el código generado automáticamente. Como losformularios incluyen muchos elementos, algunos aspectos de cada formulario deben ser 

modificados.

11.4.1. Configurando los widgets y validadores

En primer lugar se configuran los widgets y validadores generados automáticamente. El

formulario ArticleForm incluye un campo llamado slug. Un slug es una cadena decaracteres que representan de forma única a cada artículo dentro de la URL.

Si se considera por ejemplo un artículo titulado "O ptimiza las aplicaciones creadas con

Sym f  ony" , su slug sería 12-optimiza-las-aplicaciones-creadas-con-symfony , siendo

12 el valor del campo id de ese artículo. Normalmente el slug se genera automáticamente a partir del título del objeto cada vez que se guarda el objeto en la base de datos.

En el siguiente ejemplo se considera que el usuario también puede indicar de formaexplícita el valor del slug de un artículo. En este caso, aunque es obligatorio que el slug  

tenga un valor válido, no puede ser un campo obligatorio del formulario. Por este motivo se

modificar su validador para que sea opcional, tal y como muestra el listado 11-8. Además,

se modifica el campo content para aumentar su tamaño en pantalla y para forzar al usuarioa escribir al menos cinco caracteres.

Listado 11-8 - Personalizando widgets y validadores 

class ArticleForm extends BaseArticleForm{publicfunction configure(){// ...

$this->validatorSchema['slug']->setOption('required', false);$this->validatorSchema['content']->setOption('min_length', 5);

$this->widgetSchema['content']->setAttributes(array('rows' =>10, 'cols'=>40));}}

Page 103: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 103/167

 

El código anterior utiliza los objetos validatorSchema y widgetSchema como si fueranarrays de PHP. Estos arrays asociativos admiten como clave el nombre de un campo del

formulario y devuelven respectivamente el objeto validador y el objeto del widget.Mediante estos objetos se personalizan los campos y widgets individualmente.

Nota 

Para poder utilizar los objetos como si fueran arrays de PHP, las clases

sfValidatorSchema y sfWidgetFormSchema implementan la interfaz ArrayAccess,

disponible en PHP desde la versión 5.

Para asegurar que dos artículos no tengan el mismo valor en su campo slug, la definición

del esquema incluye una restricción que exige que el valor del campo sea único. Esta

restricción de la base de datos se refleja en el formulario ArticleForm mediante el

validador sfValidatorDoctrineUnique. Este validador permite asegurar que el valor deun campo del formulario sea único en la base de datos. Se puede emplear este validador,

entre otras cosas, para asegurar que una dirección de email o un login sean únicos. Ellistado 11-9 muestra como lo utiliza el formulario ArticleForm.

Listado 11-9 - Utilizando el validador sfV alidatorDoctrineUnique para asegurar que

un valor sea único 

class BaseArticleForm extends BaseFormDoctrine{publicfunction setup(){// ...

$this->validatorSchema->setPostValidator(new sfValidatorDoctrineUnique(array('model' =>'Article', 'column'=>array('slug'))));}}

El validador sfValidatorDoctrineUnique es de tipo postValidator, ya que se ejecuta

sobre los datos completos del formulario después de que cada campo individual haya sido

validado. De hecho, para validar que el valor del campo slug sea único, el validador no

sólo debe acceder al propio valor del campo slug, sino que también debe conocer el valor de la clave primaria. Además, las reglas de validación son diferentes en la fase de creación

de datos y en la de modificación, ya que el slug puede permanecer invariante mientras semodifica un artículo.

A continuación se modifica el campo active de la tabla author, que indica si un autor seencuentra activo. El listado 11-10 muestra cómo excluir a los autores inactivos en el

formulario ArticleForm, modificando la opción query del widget

sfWidgetDoctrineSelect asociado con el campo author_id. La opción query acepta un

Page 104: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 104/167

 

objeto de tipo consulta de Doctrine, lo que permite restringir los valores mostrados en la

lista desplegable asociada.

Listado 11-10 - Personalizando el widget sfW idgetDoctrineSelect 

class ArticleForm extends BaseArticleForm{publicfunction configure(){// ...

$query = Doctrine_Query::create()->from('Author a')->where('a.active = ?', true);

$this->widgetSchema['author_id']->setOption('query', $query);}}

Personalizar el widget permite restringir las opciones que se muestran en la lista

desplegable, pero no olvides que también es necesario realizar esta modificación en el

validador, tal y como muestra el listado 11-11. Al igual que el widget

sfWidgetProperSelect, el validador sfValidatorDoctrineChoice acepta una opción

llamada query que permite restringir los valores válidos para un campo del formulario.

Listado 11-11 - Personalizando el validador sfV alidatorDoctrineChoice 

class ArticleForm extends BaseArticleForm{publicfunction configure(){

// ...

$query = Doctrine_Query::create()->from('Author a')->where('a.active = ?', true);

$this->widgetSchema['author_id']->setOption('query', $query);$this->validatorSchema['author_id']->setOption('query', $query);}}

En el ejemplo anterior, se define el objeto Query directamente en el método configure().

En el proyecto que se está realizando, esta consulta puede ser útil en muchas otras

circunstancias, por lo que es mejor crear un método llamado getActiveAuthorsQuery() en la clase AuthorPeer e invocar este método desde ArticleForm, tal y como muestra ellistado 11-12.

Listado 11-12 - Refactorizando la consulta con Query en el modelo 

class AuthorTable extends Doctrine_Table{

Page 105: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 105/167

 

publicfunction getActiveAuthorsQuery(){$query = Doctrine_Query::create()

->from('Author a')->where('a.active = ?', true);

return$query;}}

class ArticleForm extends BaseArticleForm{publicfunction configure(){$authorQuery = Doctrine::getTable('Author')->getActiveAuthorsQuery();$this->widgetSchema['author_id']->setOption('query', $authorQuery);$this->validatorSchema['author_id']->setOption('query', $authorQuery);}}

Sugerencia 

De la misma forma que el widget sfWidgetDoctrineSelect y el validador 

sfValidatorDoctrineChoice representan una relación 1-n entre dos tablas, el widget

sfWidgetDoctrineSelectMany y el validador sfValidatorDoctrineChoiceMany representan una relación n-n con las mismas opciones que los anteriores. El formulario

ArticleForm utiliza estas clases para representar la relación entre las tablas article y

tag.

11.4.2. Modificando el validador

El esquema de datos define el campo email como un dato string(255), por lo que

Symfony crea un validador de tipo sfValidatorString() que restringe su longitud

máxima a 255 caracteres. Como este campo también debe cumplir la condición de que seauna dirección de correo electrónico válida, el listado 11-14 reemplaza el validador generado

automáticamente por un validador de tipo sfValidatorEmail.

Listado 11-13 - Modificando el validador del campo e mail en la clase  AuthorForm  

class AuthorForm extends BaseAuthorForm{publicfunction configure(){

$this->validatorSchema['email'] = new sfValidatorEmail();}}

11.4.3. Añadiendo un validador

En la sección anterior se modificó un validador generado automáticamente. Sin embargo,

en el caso del campo email, lo ideal sería mantener el validador que controla su longitud

máxima. En el listado 11-14 se emplea el validador sfValidatorAnd para asegurar que el

Page 106: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 106/167

 

email proporcionado sea válido y que su longitud no sea mayor que la longitud máxima

 permitida en ese campo.

Listado 11-14 - Utilizando un validador múltiple 

class AuthorForm extends BaseAuthorForm{publicfunction configure(){$this->validatorSchema['email'] = new sfValidatorAnd(array(new sfValidatorString(array('max_length' =>255)),new sfValidatorEmail(),));}}

El código del ejemplo anterior no es ideal porque si más adelante se modifica el tamaño del

campo email en el esquema de la base de datos, es necesario modificarlo también en el

formulario. Por lo tanto, en vez de reemplazar el validador generado automáticamente, esmejor añadir uno nuevo, tal y como muestra el listado 11-15.

Listado 11-15 - Añadiendo un validador 

class AuthorForm extends BaseAuthorForm{publicfunction configure(){$this->validatorSchema['email'] = new sfValidatorAnd(array($this->validatorSchema['email'],new sfValidatorEmail(),));}}

11.4.4. Modificando un widget 

En el esquema de la base de datos, la tabla article almacena el estado de cada artículo en

forma de cadena de caracteres en el campo status. Los posibles valores del estado se

definen en la clase ArticePeer, tal y como muestra el listado 11-16.

Listado 11-16 - Definiendo los posibles estados en la clase  ArticlePeer 

class ArticlePeer extends BaseArticlePeer{static protected $statuses = array('draft', 'online', 'offline');

staticpublicfunction getStatuses(){return self::$statuses;}

// ...

Page 107: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 107/167

 

}

Cuando se editan los datos de un artículo, el campo status se debería representar en forma

de lista desplegable en vez de como un cuadro de texto. Para ello, se modifica el widgetutilizado hasta el momento mediante el código mostrado en el listado 11-17.

Listado 11-17 - Modificando el widget del campo status 

class ArticleForm extends BaseArticleForm{publicfunction configure(){$this->widgetSchema['status'] = new sfWidgetFormSelect(array('choices' =>ArticlePeer::getStatuses()));}}

Para completar la modificación, también se debe cambiar el validador para asegurar que el

estado seleccionado pertenece a alguna de las posibles opciones de la lista (ver listado 11-18).

Listado 11-18 - Modificando el validador del campo status 

class ArticleForm extends BaseArticleForm{publicfunction configure(){$statuses = ArticlePeer::getStatuses();

$this->widgetSchema['status'] = new sfWidgetFormSelect(array('choices'

=>$statuses));

$this->validatorSchema['status'] = new sfValidatorChoice(array('choices'=>array_keys($statuses)));}}

11.4.5. Eliminando un campo

La tabla article dispone de dos columnas especiales llamadas created_at y

updated_at, para las cuales Doctrine actualiza automáticamente sus valores. Para evitar que el usuario las modifique, el listado 11-19 muestra cómo se pueden eliminar del

formulario.

Listado 11-19 - Eliminando un campo 

class ArticleForm extends BaseArticleForm{publicfunction configure(){unset($this->validatorSchema['created_at']);unset($this->widgetSchema['created_at']);

Page 108: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 108/167

 

 unset($this->validatorSchema['updated_at']);unset($this->widgetSchema['updated_at']);}}

Para eliminar un campo es preciso eliminar su validador y su widget. El listado 11-20muestra cómo borrar los dos con una única instrucción accediendo al formulario como si

fuera un array de PHP.

Listado 11-20 - Eliminando un campo accediendo al formulario como si fuera un

array de PHP 

class ArticleForm extends BaseArticleForm{publicfunction configure(){unset($this['created_at'], $this['updated_at']);

}}

11.4.6. Resumiendo

Los listados 11-21 y 11-22 muestran el código definitivo de los formularios ArticleForm y

AuthorForm después de personalizarlos.

Listado 11-21 - Formulario  ArticleForm  

class ArticleForm extends BaseArticleForm{

publicfunction configure(){$authorQuery = Doctrine::getTable('Author')->getActiveAuthorsQuery();

// widgets$this->widgetSchema['content']->setAttributes(array('rows' =>10, 'cols'=>40));$this->widgetSchema['status'] = new sfWidgetFormSelect(array('choices' =>ArticlePeer::getStatuses()));$this->widgetSchema['author_id']->setOption('query', $authorQuery);

// validators$this->validatorSchema['slug']->setOption('required', false);$this->validatorSchema['content']->setOption('min_length', 5);$this->validatorSchema['status'] = new sfValidatorChoice(array('choices'=>array_keys(ArticlePeer::getStatuses())));$this->validatorSchema['author_id']->setOption('query', $authorQuery);

unset($this['created_at']);unset($this['updated_at']);}}

Page 109: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 109/167

 

Listado 11-22 - Formulario  AuthorForm  

class AuthorForm extends BaseAuthorForm{publicfunction configure(){

$this->validatorSchema['email'] = new sfValidatorAnd(array($this->validatorSchema['email'],new sfValidatorEmail(),));}}

La tarea doctrine:build-forms permite generar automáticamente la mayoría deelementos de los formularios mediante la introspección del modelo de objetos. Las

 principales ventajas de esta automatización son:

y  Mejora la productividad del programador, evitándole todo el trabajo repetitivo y

redundante. De esta forma el programador sólo se encarga de personalizar los validadoresy los widgets en función de la lógica de negocio de la aplicación.

y  Si se actualiza el esquema de datos, los formularios generados también se actualizan

automáticamente. Una vez más, el programador sólo se encarga de ajustar la

personalización realizada anteriormente.

La siguiente sección trata de la modificación de las acciones y plantillas generadas por la

tarea doctrine:generate-crud.

11.5. Serialización de formularios

Las secciones anteriores explican cómo personalizar los formularios generadosautomáticamente por la tarea doctrine:build-forms. En esta sección, se personaliza elflujo de trabajo de los formularios, comenzando por el código generado mediante la tarea

doctrine:generate-crud.

11.5.1. Valores iniciales

Cada instancia de un formulario de Doctrine siempre está conectada con un objeto de

Doctrine. El objeto de Doctrine relacionado siempre pertenece a la clase que devuelve el

método getModelName(). El formulario AuthorForm de los ejemplos anteriores sólo puede

estar relacionado con objetos de la clase Author. El objeto relacionado o es un objeto vacío

(una instancia nueva de la clase Author) o es el objeto utilizado como primer argumento

del constructor. Mientras el constructor de un formulario típico utiliza como primer argumento un array de valores, el constructor de un formulario de Doctrine siempre utiliza

un objeto de Doctrine. Este objeto es el que se emplea para obtener el valor inicial de cada

campo del formulario. El método getObject() devuelve el objeto asociado con la actual

instancia del formulario y el método isNew() permite averiguar si el objeto se ha enviadomediante el constructor:

Page 110: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 110/167

 

// creando un nuevo objeto$authorForm = new AuthorForm();

print$authorForm->getObject()->getId(); // muestra nullprint$authorForm->isNew(); // muestra true

// modificando un objeto existente$author = Doctrine::getTable('Author')->find(1);$authorForm = new AuthorForm($author);

print$authorForm->getObject()->getId(); // muestra 1print$authorForm->isNew(); // muestra false

11.5.2. Flujo de trabajo

Como se explicó al principio de este capítulo, la acción edit mostrada en el listado 11-23

es la encargada de gestionar el flujo de trabajo del formulario.

Listado 11-23 - El método executeEdit del módulo author 

// apps/frontend/modules/author/actions/actions.class.phpclass authorActions extends sfActions{// ...

publicfunction executeEdit($request){$author = Doctrine::getTable('Author')->find($request->getParameter('id'));$this->form = new AuthorForm($author);

if($request->isMethod('post'))

{$this->form->bind($request->getParameter('author'));if($this->form->isValid()){$author = $this->form->save();

$this->redirect('author/edit?id='.$author->getId());}}}}

Aunque la acción edit se parece a las acciones que se han descrito en los capítulos

anteriores, existen varias diferencias:

y  El primer argumento del constructor del formulario es un objeto Doctrine de la clase

Author: 

$author = Doctrine::getTable('Author')->find($request->getParameter('id'));$this->form = new AuthorForm($author);

Page 111: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 111/167

 

y  El formato del atributo name del widget se adapta automáticamente para poder obtener

los datos enviados por el usuario mediante un array de PHP con el mismo nombre que la

tabla relacionada (author): 

$this->form->bind($request->getParameter('author'));

y  Si el formulario es válido, un simple llamada al método save() crea o actualiza el objeto

Doctrine relacionado con el formulario: 

$author = $this->form->save();

11.5.3. Creando y modificando objetos Doctrine

El código del listado 11-23 dispone de un único método para crear y modificar objetos de la

clase Author:

y  Crear nuevos objetos de tipo Author: 

o  Se invoca la acción index sin ningún parámetro id ($request->getParameter('id') es null)

o  El método find() devuelve null 

o  El objeto form se asocia con un objeto Doctrine vacío de tipo Author 

o  Si el formulario es válido, la llamada a $this->form->save() crea un nuevo

objeto de tipo Author 

y  Modificar objetos de tipo Author existentes: 

o  Se invoca la acción index con un parámetro id ($request-

>getParameter('id') es la clave primaria del objeto de tipo Author que se

quiere modificar)

El método find() devuelve el objeto de tipoAuthor relacionado con esa claveprimaria

o  El objeto form se asocia con el objeto anterior

o  Si el formulario es válido, la llamada a $this->form->save() actualiza el objeto

de tipo Author 

11.5.4. El método save()

Cuando un formulario de Doctrine es válido, el método save() actualiza el objeto

relacionado y lo almacena en la base de datos. En realidad, este método no sólo guarda elobjeto principal sino que también almacena todos los objetos relacionados. El formulario

ArticleForm por ejemplo también actualiza las etiquetas asociadas con el artículo. Comola relación entre las tablas article y tag es de tipo n-n, las etiquetas relacionadas con un

artículo se guardan en la tabla article_tag (utilizando el método saveArticleTagList() generado automáticamente).

Para asegurar la integridad de los datos guardados, el método save() realiza todas lasactualizaciones en una transacción

Page 112: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 112/167

 

Nota 

Como se explica en el capítulo 9, el método save() también actualiza las tablas

internacionalizadas.

Utilizando el método ''bindAndSave()''

El método bindAndSave() asocia los datos enviados por el usuario con el formulario,valida el formulario completo y actualiza los objetos relacionados en la base de datos, todoello en una única operación:

class articleActions extends sfActions{publicfunction executeCreate(sfWebRequest $request){$this->form = new ArticleForm();if($request->isMethod('post')&&$this->form->bindAndSave($request-

>getParameter('article'))){$this->redirect('article/created');}}}

11.5.5. Trabajando con archivos subidos

El método save() actualiza automáticamente los objetos Doctrine, pero no se encarga de

los elementos relacionados como los archivos subidos.

A continuación se adjunta un archivo a cada artículo. Los archivos subidos se almacenan en

el directorio web/uploads y en el campo file de la tabla article se almacena la rutahasta el archivo, tal y como muestra el listado 11-24.

Listado 11-24 - Esquema de la tabla article con un archivo adjunto 

// config/schema.ymldoctrine:article:

// ...file: string(255)

Cada vez que se actualiza el esquema de datos es necesario actualizar el modelo de objetos,la base de datos y los formularios:

$ ./symfony doctrine:build-all

Cuidado 

Debes tener en cuenta que la tarea doctrine:build-all borra todas las tablas de la base

de datos antes de volver a crearlas. Por lo tanto, se pierde toda la información existente en

Page 113: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 113/167

 

las tablas. Este es el motivo por el que se recomienda crear archivos con datos de prueba

(fixtures) para cargarlos cada vez que se modifica el modelo de datos.

El listado 11-25 muestra cómo modificar la clase ArticleForm para asociar un widget y un

validador con el campo file.

Listado 11-25 - Modificando el campo file del formulario  ArticleForm  

class ArticleForm extends BaseArticleForm{publicfunction configure(){// ...

$this->widgetSchema['file'] = new sfWidgetFormInputFile();$this->validatorSchema['file'] = new sfValidatorFile();}}

 No olvides que todos los formularios que permiten adjuntar archivos deben incluir un

atributo llamado enctype en la etiqueta <form>. En el capítulo 2 se explica cómo modificar 

la etiqueta <form> de la plantilla para gestionar los archivos subidos.

El listado 11-26 muestra las modificaciones necesarias para guardar el archivo subido en el

servidor y para almacenar su ruta en el objeto article.

Listado 11-26 - Guardando el objeto article y el archivo subido en la acción 

publicfunction executeEdit($request)

{$author = Doctrine::getTable('Author')->find($request->getParameter('id'));$this->form = new ArticleForm($author);

if($request->isMethod('post')){$this->form->bind($request->getParameter('article'), $request->getFiles('article'));if($this->form->isValid()){$file = $this->form->getValue('file');$filename = sha1($file->getOriginalName()).$file->getExtension($file->getOriginalExtension());

$file->save(sfConfig::get('sf_upload_dir').'/'.$filename);

$article = $this->form->save();

$this->redirect('article/edit?id='.$article->getId());}}}

Page 114: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 114/167

 

Después de guardar el archivo subido en algún directorio, el objeto sfValidatedFile ya

conoce la ruta absoluta del archivo. Cuando se invoca el método save(), se emplean los

valores de cada campo para actualizar el objeto. En el caso del campo file, el objeto

sfValidatedFile se convierte en una cadena de caracteres mediante el método

__toString() y se devuelve el valor de la ruta absoluta del archivo. A continuación, la

columna file de la tabla article almacena esta ruta absoluta.

Sugerencia 

Si sólo quieres almacenar la ruta relativa desde el directorio

sfConfig::get('sf_upload_dir'), se puede crear una clase que herede de

sfValidatedFile y que utilice la opción validated_file_class para enviar el nombre

de la nueva clase al validador sfValidatorFile. De esta forma, el validador devuelve unainstancia de tu clase. En lo que resta de capítulo se muestra otra forma de hacerlo, que

consiste en modificar el valor de la columna file antes de guardar el objeto en la base dedatos.

11.5.6. Personalizando el método ''save()''

En la sección anterior se explica cómo guardar en la acción edit un archivo subido. Uno delos principios de la programación orientada a objetos es la reutilización del código

mediante su encapsulación en clases. Por tanto, en vez de duplicar en cada acción del

formulario ArticleForm el código que guarda un archivo, es mejor mover ese código a la

clase ArticleForm. El listado 11-27 muestra como redefinir el método save() para

almacenar los archivos subidos y para borrar un archivo existente.

Listado 11-27 - Redefiniendo el método save() de la clase  ArticleForm  

class ArticleForm extends BaseFormDoctrine{// ...

publicfunction save($con = null){if(file_exists($this->getObject()->getFile())){unlink($this->getObject()->getFile());}

$file = $this->getValue('file');

$filename = sha1($file->getOriginalName()).$file->getExtension($file->getOriginalExtension());$file->save(sfConfig::get('sf_upload_dir').'/'.$filename);

return parent::save($con);}}

Page 115: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 115/167

 

Después de mover el código al formulario, la acción edit es idéntica al código generado

automáticamente por la tarea doctrine:generate-crud.

Refactorizando el código del modelo en el formulario

 Normalmente, las acciones generadas por la tarea doctrine:generate-crud no semodifican. El código que se podría añadir a la acción edit, especialmente el códigorelacionado con la serialización del formulario, normalmente se coloca en las clases del

modelo o del formulario.

Después de haber refactorizado la clase del formulario para gestionar los archivos subidos,

a continuación se muestra otro ejemplo relacionado con el modelo. El formulario

ArticleForm dispone de un campo llamado slug. Como se comentó anteriormente, el

valor de este campo se debe calcular automáticamente a partir del campo title y también puede ser definido directamente por el usuario. Esta lógica no depende del formulario sinodel modelo, tal y como muestra el siguiente código:

class Article extends BaseArticle{publicfunction save($con = null){if(!$this->getSlug()){$this->setSlugFromTitle();}return parent::save($con);}

protection function setSlugFromTitle(){// ...}}

El principal objetivo de estas refactorizaciones es el respeto a la separación entre las

diferentes capas de la aplicación y la posibilidad de reutilizar el código de las aplicaciones.

11.5.7. Personalizando el método ''doSave()''

Como se vio en las secciones anteriores, cuando se guarda un objeto se realiza unatransacción para asegurar que todas las operaciones del proceso de guardado se realizan

correctamente. Cuando se redefine el método save() como en la sección anterior al

guardar un archivo subido, el código se ejecuta de forma independiente a esa transacción.

El listado 11-28 muestra cómo utilizar el método doSave() para incluir en la transacciónglobal el código encargado de guardar el archivo subido.

Listado 11-28 - Redefiniendo el método doSave() en el formulario  ArticleForm  

class ArticleForm extends BaseFormDoctrine

Page 116: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 116/167

 

{// ...

publicfunction doSave($con = null){if(file_exists($this->getObject()->getFile())){unlink($this->getObject()->getFile());}

$file = $this->getValue('file');$filename = sha1($file->getOriginalName()).$file->getExtension($file->getOriginalExtension());$file->save(sfConfig::get('sf_upload_dir').'/'.$filename);

return parent::doSave($con);}}

Como el método doSave() se ejecuta en la transacción creada por el método save(), si la

llamada al método save() del objeto file() lanza una excepción, el objeto no se guarda.

11.5.8. Personalizando el método ''updateObject()''

En ocasiones es necesario modificar el objeto asociado al formulario después de suactualización automática pero antes de que se almacene en la base de datos.

Siguiendo con el ejemplo de los archivos subidos, en esta ocasión no se quiere almacenar 

en la columna file la ruta absoluta del archivo subido, sino que sólo se guarda la ruta

relativa respecto al directorio sfConfig::get('sf_upload_dir').

El listado 11-29 muestra cómo redefinir el método updateObject() del formulario

ArticleForm para modificar el valor de la columna file después de la actualizaciónautomática del objeto pero antes de que sea almacenado.

Listado 11-29 - Redefiniendo el método updateO bject() y la clase  ArticleForm  

class ArticleForm extends BaseFormDoctrine{// ...

publicfunction updateObject($values = null)

{$object = parent::updateObject($values);

$object->setFile(str_replace(sfConfig::get('sf_upload_dir').'/', '',$object->getFile()));

return$object;}}

Page 117: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 117/167

 

Capítulo 12. Referencia de Widgets

12.1. Introducción

El framework de formularios de Symfony incluye muchos widgets útiles que cubren lasnecesidades más comunes de la mayoría de proyectos. En este capítulo se describendetalladamente todos los widgets que incluye por defecto Symfony. También se explican

los widgets incluidos en los plugins sfFormExtraPlugin, sfPropelPlugin y

sfDoctrinePlugin, ya que estos plugins los desarrollan los creadores de Symfony eincluyen muchos widgets útiles.

Sugerencia 

Aunque no utilices el framework Symfony, puedes hacer uso de los widgets incluidos en

sfFormExtraPlugin, sfPropelPlugin y sfDoctrinePlugin simplemente copiando el

directorio widget/ de cada plugin en algún lugar de tu proyecto.

Antes de profundizar en los detalles de cada widget, veamos las características comunes detodos los widgets.

12.1.1. La clase base sfWidget 

Todos los widgets de Symfony heredan de la clase base sfWidget, que proporciona las

características comunes de todos los widgets.

Los widgets se muestran por defecto mediante XHTML, aunque se puede cambiar a HTML

con el método setXhtml():

sfWidget::setXhtml(false);

Los widgets también se encargan de aplicar de forma automática el mecanismo de escape alos atributos HTML y a todos los contenidos potencialmente peligrosos. Para ello, es

necesario conocer la codificación utilizada en el proyecto. Por defecto la codificación

utilizada es UTF-8, pero se puede configurar cualquier otra codificación mediante el método

setCharset():

sfWidget::setCharset('ISO-8859-1');

Nota 

Si utilizas los widgets de Symfony dentro de un proyecto Symfony, la codificación se

obtiene directamente a partir de su valor en el archivo de configuración settings.yml.

Si un widget necesita hojas de estilos y/o archivos de JavaScript para funcionar, también se

 pueden redefinir los métodos getJavaScripts() y getStylesheets():

Page 118: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 118/167

 

class Widget extends sfWidget{publicfunction getStylesheets(){// las claves de los arrays son las rutas de los archivos y los// valores son los nombres de los medios CSS separados por comasreturnarray('/ruta/hasta/archivo.css' =>'all','/otro/archivo.css' =>'screen,print',);}

publicfunction getJavaScripts(){returnarray('/ruta/hasta/archivo.js', '/otro/archivo.js');}}

12.1.2. La clase base sfWidgetForm

En esta sección se van a mostrar los widgets de formulario. Todos ellos heredan de la clase

 base sfWidgetForm, que a su vez extiende la clase sfWidget para proporcionar algunascaracterísticas útiles por defecto.

Cuando se crea un widget, se le pueden pasar como argumentos opcionales diferentesopciones y atributos HTML:

$w = new sfWidgetFormInput(array('default' =>'Fabien'),array('class' =>'foo'));

Las opciones y los atributos HTML también se pueden establecer con los métodos

setOptions() y setAttributes():

$w = new sfWidgetFormInput();$w->setOptions(array('default' =>'Fabien'));$w->setAttributes(array('class' =>'foo'));

Si se quiere establecer una opción o atributo HTML individual, se pueden ut ilizar los

métodos setOption() y setAttribute():

$w = new sfWidgetFormInput();

$w->setOption('default', 'Fabien');$w->setAttribute('class', 'foo');

Los widgets se muestran invocando el método render():

$w->render('nombre', 'valor', array('class' =>'foo'));

El método render() acepta los siguientes argumentos:

Page 119: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 119/167

 

y  El nombre del widget

y  El valor del widget

y  Atributos HTML opcionales (estos atributos se unen a los atributos por defecto

establecidos al construir el widget)

Nota 

Los widgets no almacenan información sobre su estado, lo que significa que una sola

instancia del widget se puede mostrar tantas veces como necesites con diferentesargumentos.

El widget anterior se muestra de la siguiente forma:

<input class="foo" type="text" name="nombre" id="nombre" />

sfWidgetForm define las siguientes opciones por defecto:

Opción Descripción

is_hidden Vale true si el widget debe ser de tipo oculto y false en cualquier otro caso

(su valor por defecto es false)

needs_multipart 

Vale true si el widget requiere que el formulario sea de tipomultipart (por

ejemplo para adjuntar archivos) y false en cualquier otro caso (su valor por

defecto es false)

default  El valor inicial que debe mostrar el widget

label El título que se debe utilizar cuando se muestra el widget mediante un

esquema de widgets

id_format El formato utilizado para generar los atributos id de HTML (su valor por

defecto es %s)

Nota 

La opción is_hidden la utilizan las clases del esquema de widgets para mostrar los widgets

ocultos sin ninguna decoración. La opción needs_multipart la utilizan las clases deformulario para añadir el atributo enctype="multipart/form-data" cuando se muestra

una etiqueta <form>.

La clase sfWidgetForm también incluye métodos accesores para todas las opciones:

y  is_hidden: métodos isHidden() y setHidden() 

y  needs_multipart: método needsMultipartForm() 

Page 120: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 120/167

 

y  default: métodos getValue() y setValue() 

y  label: métodos getLabel() y setLabel() 

y  id_format: métodos getIdFormat() y setIdFormat() 

12.1.3. Esquema de widgets

Un esquema de widgets de formulario es un widget especial que agrupa uno o varios

widgets.

En las siguientes secciones, los widgets se han agrupado en categorías para facilitar su

explicación.

12.2. Widgets

A continuación se muestra el listado completo de todos los widgets de Symfony:

y  sfWidgetFormChoice y  sfWidgetFormDate y  sfWidgetFormDateRange y  sfWidgetFormDateTime y  sfWidgetFormDoctrineChoice y  sfWidgetFormFilterInput y  sfWidgetFormFilterDate y  sfWidgetFormI18nDate y  sfWidgetFormI18nDateTime y  sfWidgetFormI18nSelectCountry y  sfWidgetFormI18nSelectLanguage y  sfWidgetFormI18nSelectCurrency y  sfWidgetFormI18nTime y  sfWidgetFormInput y  sfWidgetFormInputCheckbox y  sfWidgetFormInputFile y  sfWidgetFormInputFileEditable y  sfWidgetFormInputHidden y  sfWidgetFormInputPassword y  sfWidgetFormJQueryAutocompleter y  sfWidgetFormJQueryDate y  sfWidgetFormPropelChoice y  sfWidgetFormReCaptcha y  sfWidgetFormSchema y  sfWidgetFormSchemaDecorator y  sfWidgetFormSelect y  sfWidgetFormSelectDoubleList y  sfWidgetFormSelectMany y  sfWidgetFormSelectCheckbox y  sfWidgetFormSelectRadio y  sfWidgetFormTextarea y  sfWidgetFormTextareaTinyMCE 

Page 121: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 121/167

 

y  sfWidgetFormTime 

12.3. Widgets de tipo input

12.3.1. sfWidgetFormInput 

La etiqueta <input> es seguramente la etiqueta más sencilla que vas a utilizar en los

formularios y se representa mediante la clase sfWidgetFormInput.

Opción Descripción

type  El valor del atributo type de HTML (por defecto vale text)

$w = new sfWidgetFormInput();echo$w->render('nombre');

# <input type="text" name="nombre" id="nombre" />

12.3.2. sfWidgetFormInputCheckbox

sfWidgetFormInputCheckbox es un widget de tipo <input> cuyo atributo type es

checkbox.

$w = new sfWidgetFormInputCheckbox();echo$w->render('nombre');

# <input type="checkbox" name="nombre" id="nombre" />

12.3.3. sfWidgetFormInputHidden

sfWidgetFormInputHidden es un widget de tipo <input> cuyo atributo type vale hidden.

La opción is_hidden también se establece al valor true.

$w = new sfWidgetFormInputHidden();echo$w->render('nombre');

# <input type="hidden" name="nombre" id="nombre" />

12.3.4. sfWidgetFormInputPassword

sfWidgetFormInputPassword es un widget de tipo <input> cuyo atributo type espassword.

$w = new sfWidgetFormInputPassword();echo$w->render('nombre');

# <input type="password" name="nombre" id="nombre" />

Page 122: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 122/167

 

12.3.5. sfWidgetFormInputFile

The sfWidgetFormInputFile es un widget de tipo <input> cuyo atributo type vale file.

El valor de la opción needs_multipart se establece automáticamente a true.

$w = new sfWidgetFormInputFile();echo$w->render('nombre');

# <input type="file" name="nombre" id="nombre" />

12.3.6. sfWidgetFormInputFileEditable

sfWidgetFormInputFileEditable es un tipo de widget relacionado con los archivos, que

extiende el widget sfWidgetFormInputFile para añadir la posibilidad de mostrar oeliminar un archivo subido previamente.

Opción Descripción

file_src  La ruta web de la imagen actual (esta opción es obligatoria)

edit_mode Valor booleano que vale true si se ha habilitado la posibilidad de editar el

archivo y false en cualquier otro caso

is_image  Indica si el archivo es una imagen que se puede visualizar

with_delete  Indica si se añade un check box que permita borrar el archivo

delete_label El título que se muestra para la opción de borrar el archivo

template 

Código HTML de la plantilla utilizada para mostrar este widget. Las variables

disponibles en esta plantilla son las siguientes: 

input (el widget para subir una imagen)

delete (el checkbox para borrar el archivo)

delete_label (el título de la opción de borrado)

file (la etiqueta del archivo)

Cuidado 

En el modo edit, este widget muestra otro widget cuyo nombre se forma con el mismonombre que el widget para subir el archivo y el sufijo _delete. Por lo tanto, cuando creesun formulario, no olvides añadir también un validador para este campo adicional.

12.3.7. sfWidgetFormTextarea

sfWidgetFormTextarea es un widget simple de tipo <textarea>. Los valores de los

atributos rows y cols se establecen automáticamente porque son obligatorios.

Page 123: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 123/167

 

$w = new sfWidgetFormTextarea();echo$w->render('nombre');

# <textarea rows="4" cols="30" name="nombre" id="nombre"></textarea>

12.3.8. sfWidgetFormTextareaTinyMCE

Al contrario que el widget del <textarea> simple, sfWidgetFormTextareaTinyMCE 

muestra un editor avanzado de tipo WYSIWYG:

$w = new sfWidgetFormTextareaTinyMCE(array(),array('class' =>'foo'));

Cuidado 

Este widget es parte del plugin sfFormExtraPlugin de Symfony.

Como los archivos JavaScript del editor TinyMCE no se incluyen con el plugin, debesinstalarlos e incluirlos manualmente.

Opción Descripción

theme  El tema utilizado por Tiny MCE (por defecto se utiliza el temaadvanced)

width  La anchura del editor

height La altura del editor

config Un array con la configuración específica de JavaScript

12.4. Widgets para elecciones

12.4.1. Tipos de elecciones

Cuando el usuario debe elegir un valor entre una lista de posibilidades, HTML ofrece

diferentes formas de representar esa elección:

y  Una etiqueta <select>: 

Figura 12.1. Lista desplegable simple

y  Una etiqueta <select> con el atributo multiple: 

Page 124: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 124/167

 

 

Figura 12.2. Lista desplegable que permite seleccionar varias opciones

y  Una lista de etiquetas <input> con el atributo type igual a radio: 

Figura 12.3. Grupo de radio buttons

y  Una lista de etiquetas <input> con el atributo type igual a checkbox: 

Figura 12.4. Grupo de checkboxes

A pesar de sus diferencias, todas ellas permiten al usuario seleccionar una o varias opcionesentre una lista finita de posibilidades.

El widget sfWidgetFormChoice estandariza y agrupa todas estas variantes en un únicowidget. De esta forma, la misma elección se puede mostrar de cualquiera de las cuatro

formas mostradas anteriormente. Como se verá más adelante, también es posible definir una representación propia para la elección.

sfWidgetFormChoice es un widget especial porque delega la responsabilidad de mostrar sus contenidos a otro widget. La representación visual del widget se controla mediante las

opciones expanded y multiple:

Valor de ex pand ed  Valor de multiple  Widget utilizado 

true  true  sfWidgetFormSelectCheckbox 

true  false  sfWidgetFormSelectRadio 

false  true  sfWidgetFormSelectMany 

false  false  sfWidgetFormSelect 

Nota 

Los widgets sfWidgetFormSelect, sfWidgetFormSelectMany,

sfWidgetFormSelectCheckbox y sfWidgetFormSelectRadio que utiliza el widget

Page 125: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 125/167

 

sfWidgetFormChoice para mostrarse son widgets normales que también se pueden utilizar de forma individual. En esta sección no se documenta el uso de cada uno de estos widgets

 porque casi siempre es mejor utilizar el widget sfWidgetFormChoice, que es mucho másflexible.

A continuación se muestra la representación HTML de cada widget:

$w = new sfWidgetFormChoice(array('choices' =>array('Fabien Potencier', 'Fabian Lange'),));

Figura 12.5. Lista desplegable simple

$w = new sfWidgetFormChoice(array('multiple' =>true,'choices' =>array('PHP', 'symfony', 'Doctrine', 'Propel', 'model'),));

Figura 12.6. Lista desplegable que permite seleccionar varias opciones

$w = new sfWidgetFormChoice(array('expanded' =>true,

'choices' =>array('published', 'draft', 'deleted'),));

Figura 12.7. Grupo de radio buttons

$w = new sfWidgetFormChoice(array('expanded' =>true,'multiple' =>true,'choices' =>array('A week of symfony', 'Call the expert', 'Community'),));

Figura 12.8. Grupo de checkboxes

12.4.2. Agrupación de opciones

El widget sfWidgetFormChoice también soporta la agrupación de opciones cuando se pasa

un array de arrays como valor de la opción choices:

Page 126: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 126/167

 

$choices = array('Europe' =>array('France' =>'France', 'Spain' =>'Spain', 'Italy'=>'Italy'),'America' =>array('USA' =>'USA', 'Canada' =>'Canada', 'Brazil'=>'Brazil'),);

$w = new sfWidgetFormChoice(array('choices' =>$choices));

Figura 12.9. Agrupación de opciones

Las opciones expanded y multiple siguen funcionando como antes:

$w = new sfWidgetFormChoice(array('choices' =>$choices,'expanded' =>true,));

Figura 12.10. Agrupación de opciones expandidas

La plantilla utilizada para mostrar el widget también se puede personalizar:

$w = new sfWidgetFormChoice(array(

'choices' =>$choices,'expanded' =>true,'renderer_options' =>array('template' =>'<strong>%group%</strong>%options%'),));

Page 127: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 127/167

 

 

Figura 12.11. Agrupación de opciones expandidas y con formato personalizado

A continuación se muestran otros ejemplos de combinaciones de opciones:

$w = new sfWidgetFormChoice(array('choices' =>$choices,'multiple' =>true,

));

Figura 12.12. Agrupación de opciones con selección múltiple

$w = new sfWidgetFormChoice(array('choices' =>$choices,'multiple' =>true,'expanded' =>true,'renderer_options' =>array('template' =>'<strong>%group%</strong>%options%'),));

Figura 12.13. Agrupación de opciones expandida y con selección múltiple

Nota 

Page 128: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 128/167

 

Cuando el widget se muestra con una etiqueta <select> simple, se utiliza la etiqueta

<optgroup> estándar.

12.4.3. Opciones soportadas

La siguiente tabla muestra todas las opciones soportadas por el widget:

Opción Descripción

choices  Array con los valores de las opciones (obligatorio)

multiple  true si la etiqueta <select> permite selecciones múltiples

expanded  true para mostrar el widget de forma expandida

renderer_class  La clase que se utiliza en vez de la clase por defecto

renderer_options Las opciones que se pasan a la clase que muestra el widget

renderer 

El widget utilizado para mostrar las opciones (si se utiliza esta opción, se

ignoran las opciones expanded y renderer_options)

La opción choices será: new sfCallable($thisWidgetInstance,

'getChoices') 

Los widgets sfWidgetFormSelectCheckbox y sfWidgetFormSelectRadio soportan las

siguientes opciones:

Opción Descripción

label_separator El separador que se utiliza entre el <input> del checkbox/radio button y el

título de la opción

class  El atributo class que se utiliza en la etiqueta <ul> principal

separator  El separador que se utiliza entre los <input> de cada checkbox/radio button

formatter  Código que se ejecuta para mostrar las opciones del checkbox.El código recibe como argumentos el widget y el array de etiquetas <input> 

template La plantilla que se utiliza al agrupar las opciones (puede hacer uso de las

variables %group% y %options%)

Sugerencia 

Page 129: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 129/167

 

El widget sfWidgetFormChoiceMany es en realidad un atajo del widget

sfWidgetFormChoice con la opción multiple establecida automáticamente a true.

12.4.4. Representación mediante una lista doble

Cuando el usuario puede seleccionar varias opciones, normalmente es mejor mostrar lasopciones seleccionadas en otra lista. El widget sfWidgetFormSelectDoubleList permitemostrar la selección de opciones en forma de lista doble:

$w = new sfWidgetFormChoice(array('choices' =>array('PHP', 'symfony', 'Doctrine', 'Propel','model'),'renderer_class' =>'sfWidgetFormSelectDoubleList',));

Figura 12.14. Lista doble

Cuidado 

Este widget forma parte del plugin sfFormExtraPlugin.

Nota 

Este widget requiere el uso de JavaScript para su funcionamiento. Si quieres obtener las

rutas de los archivos JavaScript, puedes emplear el método getJavaScripts() del widget:

$rutasArchivos = $w->getJavascripts();

Opción Descripción

choices  Array con los valores de las opciones (obligatorio)

class  El atributo class principal del widget

class_select  El atributo class que se utiliza en las dos etiquetas <select> 

Page 130: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 130/167

 

label_unassociated El título de las opciones no seleccionadas

label_associated  El título de las opciones seleccionadas

unassociate  El código HTML del enlace para deseleccionar una opción

associate  El código HTML del enlace para seleccionar una opción

template 

El código HTML de la plantilla que se utiliza para mostrar el widget.

Las variables disponibles en la plantilla son: %label_associated%,

%label_unassociated%, %associate%, %unassociate%,

%associated%, %unassociated%, %class% 

12.4.5. Autocompletado

Cuando el usuario debe seleccionar un valor entre un gran número de opciones, crear unalista enorme con todas las opciones no es práctico. El widget

sfWidgetFormJQueryAutocompleter soluciona este problema convirtiendo una etiqueta

<input> simple en un elemento que autocompleta el texto escrito por el usuario.

Cuidado 

Este widget es parte del plugin sfFormExtraPlugin. Como los archivos de las librerías

JQuery y JQuery UI no se incluyen en sfFormExtraPlugin, debes instalarlos e incluirlos amano.

$w = new sfWidgetFormChoice(array('choices' =>array(),'renderer_class' =>'sfWidgetFormJQueryAutocompleter','renderer_options' =>array('url' =>'/script_autocompletado'),));

Nota 

Este widget requiere el uso de algunas hojas de estilos y archivos JavaScript para su

funcionamiento. Los métodos getJavaScripts() y getStylesheets() permiten obtener las rutas de todos estos archivos.

La opción url es la URL a la que accede el widget para obtener la lista de sugerencias a partir del texto introducido por el usuario. Esta URL incluye dos parámetros:

y  q: la cadena de texto escrita por el usuario

y  limit: el máximo número de sugerencias que debe devolver el script

Page 131: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 131/167

 

La respuesta del script debe ser una representación JSO N válida del array de opciones

(puedes utilizar la función json_encode() de PHP para convertir un array al formatoJSO N).

Opción Descripción

url  La URL a la que se accede para obtener las opciones (obligatorio)

config  Array de JavaScript que configura el widget de autocompletado de JQuery

value_callback Código que se ejecuta sobre cada valor antes de mostrarlo

Si la lista de sugerencias está relacionada con un modelo de Propel, es decir, con una tabla

de la base de datos, puedes utilizar el widget sfWidgetFormPropelJQueryAutocompleter,que está optimizado para búsquedas a partir de la clave primaria:

$w = new sfWidgetFormChoice(array('renderer_class' =>'sfWidgetFormPropelJQueryAutocompleter','renderer_options' =>array('model' =>'Articulo','url' =>'/script_autocompletado',),));

Opción Descripción

model  La clase del modelo (obligatoria)

method El método que se utiliza para convertir el objeto en una cadena de texto (__toString() por defecto)

12.4.6. Elecciones asociadas con modelos de Propel

Si las opciones están relacionadas con un modelo de Propel (por ejemplo cuando el usuario

 puede cambiar una clave externa) puedes utilizar el widget sfWidgetFormPropelChoice:

$w = new sfWidgetFormPropelChoice(array('model' =>'Articulo','add_empty' =>false,));

El widget obtiene automáticamente las opciones (choices) a partir de la clase del modelo

(model). Este widget se puede configurar mediante las siguientes opciones:

Opción Descripción

Page 132: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 132/167

 

model  La clase del modelo Propel (obligatoria)

add_empty Indica si se debe mostrar un primer elemento vacío en la lista (false por defecto)

Si el valor no es de tipo booleano, se utiliza como texto de la opción

method  El método utilizado para mostrar el valor de los objetos (__toString por defecto)

key_method El método utilizado para mostrar la clave de los objetos (getPrimaryKey por

defecto)

order_by 

Array compuesto por dos opciones: 

1) La columna por la que se ordenan los resultados (debe indicarse con el formato

PhpName)

2) Criterio de ordenación (asc o desc)

criteria  ObjetoCriteria

que se utiliza para obtener los objetos

connection  El nombre de la conexión Propel que se utiliza (null por defecto)

multiple  true si la etiqueta <select> permite selecciones múltiples

peer_method El método peer utilizado para obtener los objetos

12.4.7. Elecciones asociadas con modelos de Doctrine

Si las opciones están relacionadas con un modelo de Doctrine (por ejemplo cuando elusuario puede cambiar una clave externa) puedes utilizar el widget

sfWidgetFormDoctrineChoice:

$w = new sfWidgetFormDoctrineChoice(array('model' =>'Articulo','add_empty' =>false,));

El widget obtiene automáticamente las opciones (choices) a partir de la clase del modelo

(model). Este widget se puede configurar mediante las siguientes opciones:

Opción Descripción

model  La clase del modelo (obligatoria)

add_empty 

Indica si se debe mostrar un primer elemento vacío en la lista (false por

defecto)

Si el valor no es de tipo booleano, se utiliza como texto de la opción

Page 133: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 133/167

 

method El método utilizado para mostrar el valor de los objetos (__toString por

defecto)

key_method El método utilizado para mostrar la clave de los objetos (getPrimaryKey por

defecto)

order_by 

Array compuesto por dos opciones: 

1) La columna por la que se ordenan los resultados (debe indicarse con el formato

PhpName)

2) Criterio de ordenación (asc o desc)

query  Consulta que se utiliza para obtener los objetos

connection  El nombre de la conexión Doctrine que se utiliza (null por defecto)

multiple true

si la etiqueta<select>

permite selecciones múltiples

table_method El método utilizado para obtener los objetos

12.5. Widgets para fechas

Los widgets para fechas se pueden utilizar para facilitar la introducción de fechas, ya queutilizan varios elementos para introducir sólo la fecha, sólo la hora o la fecha y hora. Todos

los widgets de fechas se representan mediante varias etiquetas HTML. Además estoswidgets se pueden personalizar en función de la cultura del usuario.

Nota 

Algunos usuarios prefieren una sola etiqueta <input> para introducir fechas porque es

mucho más rápido que utilizar varias listas desplegables de tipo <select>. Cuando se

utiliza un solo <input>, el formato de la fecha se comprueba en el servidor durante el proceso de validación. El validador de fechas del framework Symfony incluye validaciones

muy avanzadas que permiten que el usuario introduzca las fechas con formatos muydiferentes.

12.5.1. sfWidgetFormDate

sfWidgetFormDate muestra un widget para seleccionar la fecha:

Figura 12.15. Widget para seleccionar la fecha

Page 134: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 134/167

 

Los valores enviados por el usuario se guardan en un array con el mismo nombre que el

widget:

$w = new sfWidgetFormDate();$w->render('fecha');

# los valores enviados por el usuario se encuentran en# un array llamado "fecha"# array(# 'fecha' => array(# 'day' => 15,# 'month' => 10,# 'year' => 2005,# ),# );

El comportamiento del widget se puede personalizar mediante las siguientes opciones:

Opción Descripción

format Cadena de texto con el formato de la fecha (por defecto es

%month%/%day%/%year%)

years  Array con los años que se muestran en la lista para seleccionar un año (opcional)

months Array con los meses que se muestran en la lista para seleccionar un mes

(opcional)

days  Array con los días que se muestran en la lista para seleccionar un día (opcional)

can_be_empty Indica si el widget admite fechas vacías (true por defecto)

empty_values 

Array con los textos que se utilizan para los valores vacíos de las listas

desplegables

(por defecto se utiliza una cadena de texto vacía para todos los campos)

La opción format permite modificar la distribución por defecto de las etiquetas (al mostrar 

el widget, las variables %year%, %month% y %day% se sustituyen por sus correspondientes

etiquetas <select>):

$w = new sfWidgetFormDate(array('format' =>'%year% - %month% - %day%'));

Page 135: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 135/167

 

 

Figura 12.16. Widget personalizado para seleccionar la fecha

La etiqueta <select> de los años (year) muestra por defecto los diez años alrededor del

año actual. La opción years permite modificar este comportamiento:

$years = range(2009, 2020);$w = new sfWidgetFormDate(array('years' => array_combine($years, $years)));

Las opciones years, months y days toman como argumento un array cuyas claves son losvalores de las etiquetas <option> y cuyos valores son las cadenas de texto que se muestranal usuario.

12.5.2. sfWidgetFormTime

sfWidgetFormTime muestra un widget para seleccionar una hora:

Figura 12.17. Widget para seleccionar la hora

Los valores enviados por el usuario se guardan en un array con el mismo nombre que elwidget:

$w = new sfWidgetFormTime();$w->render('hora');

# los valores enviados por el usuario se encuentran en# un array llamado "hora"# array(# 'hora' => array(

# 'hour' => 12,# 'minute' => 13,# 'second' => 14,# ),# );

El comportamiento del widget se puede personalizar mediante las siguientes opciones:

Page 136: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 136/167

 

Opción Descripción

format Cadena de texto con el formato de la hora (por defecto es

%hour%:%minute%:%second%)

format_without_seconds Cadena de texto con el formato de la hora sin segundos (por defectoes %hour%:%minute%)

with_seconds Indica si se muestra una lista para seleccionar los segundos (false 

por defecto )

hours Array con las horas que se muestran en la lista para seleccionar una

hora (opcional)

minutes Array con los minutos que se muestran en la lista para seleccionar

un minuto (opcional)

seconds Array con los segundos que se muestran en la lista para seleccionar

los segundos (opcional)

can_be_empty  Indica si el widget admite horas vacías (true por defecto)

empty_values 

Array con los textos que se utilizan para los valores vacíos de las

listas desplegables

(por defecto se utiliza una cadena de texto vacía para todos los

campos)

Por defecto este widget no permite elegir los segundos de la hora, pero este

comportamiento se puede modificar estableciendo la opción with_seconds a true:

$w = new sfWidgetFormTime(array('with_seconds' =>true));

Las opciones format y format_without_seconds permiten modificar la distribución por 

defecto de las etiquetas (al mostrar el widget, las variables %hour%, %minute% y %second% 

se sustituyen por sus correspondientes etiquetas <select>):

$w = new sfWidgetFormTime(array('with_seconds' =>true,'format' =>'%hour% : %minute% : %second%',));

Figura 12.18. Widget personalizado para seleccionar la hora

Page 137: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 137/167

 

Si no quieres que el usuario pueda elegir cualquier valor de las listas desplegables, puedes

indicar los valores seleccionables para cada lista:

$segundos = array(0, 15, 30, 45);$w = new sfWidgetFormTime(array('with_seconds' =>true,

'seconds' => array_combine($segundos, $segundos),));

Figura 12.19. Widget para seleccionar la hora con los segundos personalizados

Las opciones hours, minutes y seconds toman como argumento un array cuyas claves sonlos valores de las etiquetas <option> y cuyos valores son las cadenas de texto que semuestran al usuario.

12.5.3. sfWidgetFormDateTime

sfWidgetFormDateTime es un widget especial que muestra dos sub-widgets: un widget de

tipo sfWidgetFormDate y otro widget de tipo sfWidgetFormTime:

$w = new sfWidgetFormDateTime();

Figura 12.20. Widget para seleccionar la fecha y la hora

Opción Descripción

date  Opciones para el widget de la fecha (ver opciones de sfWidgetFormDate)

time  Opciones para el widget de la hora (ver opciones de sfWidgetFormTime)

with_time Indica si se muestra el widget de la hora (true por defecto)

format  Cadena de texto con el formato de la fecha y hora (por defecto es %date% %time%)

Sugerencia 

Page 138: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 138/167

 

Este widget crea por defecto instancias de sfWidgetFormDate y sfWidgetFormTime paramostrar la fecha y la hora. Si quieres cambiar las clases utilizadas por el widget, puedes

redefinir los métodos getDateWidget() y getTimeWidget().

12.5.4. sfWidgetFormI18nDate

sfWidgetFormI18nDate extiende el widget sfWidgetFormDate. Mientras que el widget

estándar muestra los meses como números, este widget muestra los meses como cadenas detexto y traducidas en función de la cultura del usuario:

$w = new sfWidgetFormI18nDate(array('culture' =>'fr'));

Figura 12.21. Widget internacionalizado para seleccionar la fecha

El formato de los meses se puede configurar mediante la opción month_format, que acepta

los siguientes tres valores: name (valor por defecto, que muestra el nombre completo delmes), short_name (abreviatura del nombre del mes) o number (número del mes).

$w = new sfWidgetFormI18nDate(array('culture' =>'fr','month_format' =>'short_name',));

Page 139: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 139/167

 

Figura 12.22. Widget internacionalizado para seleccionar la fecha con los meses abreviados

El widget también tiene en cuenta la cultura del usuario en el orden en el que se muestran

las tres listas desplegables y en el separador utilizado entre ellas.

Cuidado 

Este widget depende del sub-framework de internacionalización de Symfony.

12.5.5. sfWidgetFormI18nTime

sfWidgetFormI18nTime extiende el widget sfWidgetFormTime estándar. En función de la

cultura que se le pasa como opción culture, el widget determina el orden en el que semuestran las tres listas desplegables y el separador utilizado entre ellas.

$w = new sfWidgetFormI18nTime(array('culture' =>'ar'));

Figura 12.23. Widget internacionalizado para seleccionar la hora

Cuidado 

Este widget depende del sub-framework de internacionalización de Symfony.

12.5.6. sfWidgetFormI18nDateTime

sfWidgetFormI18nDateTime es un widget especial que muestra dos sub-widgets: un

widget de tipo sfWidgetFormI18nDate y otro widget de tipo sfWidgetFormI18nTime.

Cuidado 

Este widget depende del sub-framework de internacionalización de Symfony.

12.5.7. sfWidgetFormDataRange

sfWidgetFormDateRange es un widget que permite seleccionar un rango de fechas:

$w = new sfWidgetFormDateRange(array('from_date' =>new sfWidgetFormDate(),'to_date' =>new sfWidgetFormDate(),));

Figura 12.24. Widget para seleccionar un rango de fechas

Page 140: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 140/167

 

Opción Descripción

from_date El widget para la fecha inicial (obligatorio)

to_date  El widget para la fecha final (obligatorio)

template La plantilla que se utiliza para mostrar el widget

(la plantilla puede hacer uso de las variables %from_date% y %to_date% 

La opción template se puede emplear para modificar la plantilla que utiliza el wdiget para

mostrar sus contenidos:

$w = new sfWidgetFormDateRange(array('from_date' =>new sfWidgetFormDate(),'to_date' =>new sfWidgetFormDate(),'template' =>'Begin at: %from_date%<br />End at: %to_date%',));

Figura 12.25. Widget personalizado para seleccionar un rango de fechas

Nota 

Este widget es la clase base de un widget más avanzado llamado

sfWidgetFormFilterDate.

12.5.8. sfWidgetFormJQueryDate

sfWidgetFormJQueryDate utiliza la librería JQuery UI para mostrar un widget que permite

seleccionar una fecha:

$w = new sfWidgetFormJQueryDate(array('culture' =>'en',));

Cuidado 

Este widget es parte del plugin sfFormExtraPlugin. Como los archivos de las librerías

JQuery y JQuery UI no se incluyen en sfFormExtraPlugin, debes instalarlos e incluirlos amano.

Opción Descripción

Page 141: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 141/167

 

image  La ruta de la imagen que representa el widget (false por defecto)

config  Array de JavaScript con la configuración del widget de JQuery

culture La cultura del usuario

12.6. Widgets de internacionalización

Cuidado 

Los widgets de esta sección dependen del sub-framework de internacionalización deSymfony.

12.6.1. sfWidgetFormI18nSelectCountry

sfWidgetFormI18nSelectCountry muestra una lista para seleccionar un país:

$w = new sfWidgetFormI18nSelectCountry(array('culture' =>'fr'));

Figura 12.26. Widget internacionalizado para seleccionar un país

Opción Descripción

culture  La cultura que se utiliza para mostrar el nombre de los países (obligatorio)

countries Array con los códigos de los países que se deben mostrar en la lista (los códigos se

Page 142: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 142/167

 

corresponden con el estándar ISO 3166)

add_empty Indica si se debe mostrar un primer elemento vacío en la lista (false por defecto)

Si el valor no es de tipo booleano, se utiliza como texto de la opción

12.6.2. sfWidgetFormI18nSelectLanguage

sfWidgetFormI18nSelectLanguage muestra una lista para seleccionar un idioma:

$w = new sfWidgetFormI18nSelectLanguage(array('culture' =>'fr'));

Figura 12.27. Widget internacionalizado para seleccionar un idioma

Opción Descripción

culture  La cultura que se utiliza para mostrar el nombre de los idiomas (obligatorio)

languages Array con los códigos de los idiomas que se deben mostrar en la lista (los códigos se

corresponden con el estándar ISO 639-1)

add_empty Indica si se debe mostrar un primer elemento vacío en la lista (false por defecto)

Si el valor no es de tipo booleano, se utiliza como texto de la opción

12.6.3. sfWidgetFormI18nSelectCurrency

sfWidgetFormI18nSelectCurrency muestra una lista para seleccionar una divisa:

Page 143: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 143/167

 

$w = new sfWidgetFormI18nSelectCurrency(array('culture' =>'fr'));

Figura 12.28. Widget internacionalizado para seleccionar una divisa

Opción Descripción

culture  La cultura que se utiliza para mostrar el nombre de las divisas (obligatorio)

currencies Array con los códigos de las divisas que se deben mostrar en la lista

add_empty Indica si se debe mostrar un primer elemento vacío en la lista (false por defecto)

Si el valor no es de tipo booleano, se utiliza como texto de la opción

12.7. Widget para CAPTCHA

El plugin sfFormExtraPlugin incluye un widget para mostrar CAPTCHA llamado

sfWidgetFormReCaptcha y basado en el proyecto ReCaptcha:

$w = new sfWidgetFormReCaptcha(array(

'public_key' =>'CLAVE_PUBLICA_DE_RECAPTCHA'));

Opción Descripción

public_key  La clave pública de ReCaptcha

use_ssl  Indica si se utiliza SSL en la conexión (false por defecto)

Page 144: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 144/167

 

server_url  La URL de la APIHTTP 

server_url_ssl La URL de la API HTTPS (sólo se utiliza si la opción use_ssl es true)

La opción public_key es la clave pública de ReCaptcha, que puedes solicitar gratuitamente desde la página para solicitar claves de la API.

Sugerencia 

Si necesitas ayuda sobre este servicio, puedes consultar la documentación de la API deReCaptcha.

Como no se puede cambiar el nombre de los campos de ReCaptcha, tienes que añadirlosmanualmente al asociar un formulario enviado mediante una petición HTTP.

Si por ejemplo el nombre de los campos de tu formulario sigue el formato contacto[%s],este es el código que necesitas para asegurar que la información del CAPTCHA se añade al

resto de valores enviados mediante el formulario:

$captcha = array('recaptcha_challenge_field' =>$request->getParameter('recaptcha_challenge_field'),'recaptcha_response_field' =>$request->getParameter('recaptcha_response_field'),);$valoresEnviados = array_merge($request->getParameter('contacto'),array('captcha' =>$captcha));

Este widget hace uso del validador sfValidatorReCatpcha.

12.8. Widgets para filtros

Los widgets para filtros son un tipo de especial de widget que se pueden utilizar para

mostrar un formulario como si fuera un filtro.

12.8.1. sfWidgetFormFilterInput 

sfWidgetFormFilterInput muestra un filtro para texto. Por defecto incluye un checkbox

que permite al usuario buscar textos vacíos.

Opción Descripción

with_empty  Indica si se añade el checkbox para buscar textos vacíos (true por defecto)

Page 145: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 145/167

 

empty_label El título del checkbox que permite buscar textos vacíos

template 

La plantilla que se utiliza para mostrar el widget

(las variables que puede utilizar la plantilla son %input%, %empty_checkbox% y

%empty_label%)

12.8.2. sfWidgetFormFilterDate

sfWidgetFormFilterDate muestra un filtro para seleccionar un rango de fechas. Por defecto incluye un checkbox que permite al usuario buscar fechas vacías.

Opción Descripción

with_empty  Indica si se añade el checkbox para buscar fechas vacías (true por defecto)

empty_label El título del checkbox que permite buscar fechas vacías

template 

La plantilla que se utiliza para mostrar el widget

(las variables que puede utilizar la plantilla son %date_range%,

%empty_checkbox% y %empty_label%)

12.9. sfWidgetFormSchema

sfWidgetFormSchema es un tipo de widget especial compuesto por varios campos de

formulario. Un campo es simplemente un widget asociado a un nombre:

$w = new sfWidgetFormSchema(array('nombre' =>new sfWidgetFormInput(),'pais' =>new sfWidgetFormI18nSelectCountry(),));

Nota 

Los formularios se definen mediante un esquema de widgets de tipo sfWidgetFormSchema.

El constructor de sfWidgetFormSchema acepta cinco argumentos opcionales:

y  Un array de campos de formulario

y  Un array de opciones

y  Un array de atributos HTML

y  Un array de títulos para los widgets del esquema

y  Un array de mensajes de ayuda para los widgets del esquema

Las opciones disponibles son las siguientes:

Page 146: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 146/167

 

Opción Descripción

name_format El formato que siguen los nombres de los campos (se debe utilizar la notación

de sprintf y su valor por defecto es %s)

form_formatter El nombre del formato del formulario (Symfony incluye los formatos table ylist, siendo table el que se utiliza por defecto)

Si quieres modificar el formato por defecto de todos los formularios, puedes utilizar el

método setDefaultFormFormatterName():

sfWidgetFormSchema::setDefaultFormFormatterName('list');

Como sfWidgetFormSchema extiende la clase sfWidgetForm, hereda todos sus métodos ycaracterísticas.

Cuidado 

Los objetos sfWidgetFormSchema solamente muestran las filas que contienen los widgets y

no la etiqueta que encierra a todos ellos (<table> en el formato de tablas y <ul> en elformato de listas):

<table><?phpecho$ws->render('')?></table>

Se puede emplear sfWidgetFormSchema como si fuera un array para acceder a todos loswidgets que incluye:

$ws = new sfWidgetFormSchema(array('nombre' =>new sfWidgetFormInput()));

$widgetNombre = $ws['nombre'];

unset($ws['nombre']);

Cuidado 

Cuando un formulario incluye un esquema de widgets, el formulario te da acceso al campoasociado de la plantilla, pero no al propio widget, tal y como se explica en capítulos

anteriores.

Los esquemas de widgets también se pueden anidar como cualquier otro tipo de widget:

$ws = new sfWidgetFormSchema(array('titulo' =>new sfWidgetFormInput(),'autor' =>new sfWidgetFormSchema(array('nombre' =>new sfWidgetFormInput(),'apellidos' =>new sfWidgetFormInput(),

Page 147: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 147/167

 

)),));

Para acceder a los esquemas de widgets anidados, se puede utilizar la notación de losarrays:

$ws['autor']['nombre']->setLabel('Nombre');

A continuación se describen los principales métodos de las clases de los esquemas de

widgets. La documentación de la API de Symfony dispone de la lista completa de métodos

y todas sus características.

12.9.1. Métodos setLabel(), getLabel(), setLabels() y getLabels()

Los métodos setLabel(), getLabel(), setLabels() y getLabels() controlan los títulos

de los widgets incluidos en el esquema. En realidad, estos métodos son atajos de los

métodos getLabel() y setLabel() de los widgets.

$ws = new sfWidgetFormSchema(array('nombre' =>new sfWidgetFormInput()));

$ws->setLabel('nombre', 'Fabien');

// que es equivalente a...$ws['nombre']->setLabel('Fabien');

// y también es equivalente a...$ws->setLabel(array('nombre' =>'Fabien'));

El método setLabels() fusiona los valores indicados con los valores existentes.

12.9.2. Métodos setDefault(), getDefault(), setDefaults() y getDefaults()

Los métodos setDefault(), getDefault(), setDefaults() y getDefaults() controlan

los valores por defecto de los widgets incluidos en el esquema. Estos métodos son atajos de

los métodos getDefault() y setDefault() de los widgets.

$ws = new sfWidgetFormSchema(array('nombre' =>new sfWidgetFormInput()));

$ws->setDefault('nombre', 'Fabien');

// que es equivalente a...

$ws['nombre']->setDefault('Fabien');

// y también es equivalente a...$ws->setDefaults(array('nombre' =>'Fabien'));

El método setDefaults() fusiona los valores indicados con los valores existentes.

Page 148: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 148/167

 

12.9.3. Métodos setHelp(), setHelps(), getHelps() y getHelp()

Los métodos setHelp(), setHelps(), getHelps() y getHelp() controlan los mensajes

de ayuda asociados con los widgets incluidos en el esquema:

$ws = new sfWidgetFormSchema(array('nombre' =>new sfWidgetFormInput()));

$ws->setHelp('nombre', 'Fabien');

// que es equivalente a...$ws->setHelps(array('nombre' =>'Fabien'));

El método setHelps() fusiona los valores indicados con los valores existentes.

12.9.4. Métodos getPositions(), setPositions() y moveField()

Los campos que se incluyen en un esquema de widgets se encuentran ordenados. El orden

se puede modificar con el método moveField():

$ws = new sfWidgetFormSchema(array('nombre' =>new sfWidgetFormInput(),'apellidos' =>new sfWidgetFormInput()));

$ws->moveField('nombre', sfWidgetFormSchema::AFTER, 'apellidos');

Las constantes definidas para mover los widgets son las siguientes:

y  sfWidgetFormSchema::FIRST 

y sfWidgetFormSchema::LAST 

y  sfWidgetFormSchema::BEFORE 

y  sfWidgetFormSchema::AFTER 

También se puede utilizar el método setPositions() para modificar todas las posiciones:

$ws->setPositions(array('apellidos', 'nombre'));

12.9.5. sfWidgetFormSchemaDecorator

sfWidgetFormSchemaDecorator es un tipo especial de esquema de widgets que permite

decorar un esquema de widgets con el código HTML indicado.

$ws = new sfWidgetFormSchema(array('nombre' =>new sfWidgetFormInput()));

$wd = new sfWidgetFormSchemaDecorator($ws, '<table>%content%</table>');

Nota 

Este widget lo utiliza Symfony internamente cuando un formulario se incluye dentro deotro formulario.

Page 149: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 149/167

 

Capítulo 13. Validadores

13.1. Introducción

El framework de formularios de Symfony incluye muchos validadores útiles que cubren lasnecesidades comunes de la mayoría de proyectos. En este capítulo se describendetalladamente todos los validadores que incluye por defecto Symfony. También se

explican los validadores incluidos en los plugins sfPropelPlugin y sfDoctrinePlugin,ya que estos plugins los desarrollan los creadores de Symfony e incluyen muchos

validadores útiles.

Sugerencia 

Aunque no utilices el framework Symfony, puedes hacer uso de los validadores incluidos

en sfFormExtraPlugin, sfPropelPlugin y sfDoctrinePlugin simplemente copiando el

directorio validator/ de cada plugin en algún lugar de tu proyecto.

Antes de profundizar en los detalles de cada validador, veamos las características comunes

de todos los validadores.

13.1.1. La clase base sfValidatorBase

Todos los validadores de Symfony heredan de la clase base sfValidator, que proporciona

las características comunes de todos los validadores.

La finalidad de los validadores consiste en limpiar y validar los valores originales.

Cuando se crea un validador, se le pueden pasar como argumentos opcionales diferentesopciones y mensajes de error:

$v = new sfValidatorString(array('required' =>true),array('required' =>'Este valor es obligatorio.'));

Las opciones y los mensajes de error también se pueden establecer con los métodos

setOptions() y setMessages():

$v = new sfValidatorString();$v->setOptions(array('required' =>true));$v->setMessages(array('required' =>'Este valor es obligatorio.'));

Si se quiere establecer una opción o mensaje de error individual, se pueden utilizar los

métodos setOption() y setMessage():

$v = new sfValidatorString();$v->setOption('required', true);

Page 150: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 150/167

 

$v->setMessage('required', 'Este valor es obligatorio.');

Los valores originales se pueden validar con el método clean():

$valorLimpio = $v->clean('nombre', 'valor', array('class' =>'foo'));

El método clean() toma como argumento el valor original y devuelve el valor limpio. Si

se produce un error de valiación, se lanza una excepción de tipo sfValidatorError.

Nota 

Los validadores no almacenan información sobre su estado, lo que significa que una solainstancia de un validador puede validar tantos valores como necesites.

sfValidatorBase define las siguientes opciones por defecto:

Opción Error Descripción

required  required Vale true si el valor es obligatorio y false en cualquier otro caso (su

valor por defecto es true)

trim  -

Vale true si se deben eliminar los espacios en blanco del principio y del

final del valor y false en cualquier otro caso (su valor por defecto es

false)

empty_value - Valor vacío que se devuelve cuando el valor no es obligatorio

sfValidatorBase también define los siguientes mensajes de error por defecto:

Error Descripción

required Mensaje de error que se muestra cuando el valor original está vacío pero es obligatorio

(por defecto el mensaje que se muestra esRequired)

invalid Mensaje de error genérico que se muestra cuando se produce un error (por defecto el

mensaje que se muestra esInvalid)

Para modificar los mensajes por defecto de los errores required y invalid, puedes utilizar 

los métodos setRequiredMessage() y setInvalidMessage():

sfValidatorBase::setRequiredMessage('Este valor es obligatorio.');sfValidatorBase::setInvalidMessage('Este valor no es válido.');

Page 151: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 151/167

 

Los mensajes de error pueden contener variables en forma de cadenas de texto encerradas

 por %. Las variables se sustituyen por sus valores durante la ejecución de la aplicación.Todos los mensajes de error tienen acceso directo al valor original mediante una variable

llamada %value%. Además, los mensajes de error pueden definir sus propias variables.

Nota 

En la siguiente sección no se menciona la variable %value% porque siempre está disponible.

Algunos validadores necesitan conocer la codificación que utiliza el valor original. Por 

defecto la codificación utilizada es UTF-8, pero se puede configurar cualquier otra

codificación mediante el método setCharset():

sfValidatorBase::setCharset('ISO-8859-1');

Nota 

Si utilizas los validadores de Symfony dentro de un proyecto Symfony, la codificación seobtiene directamente del archivo de configuración settings.yml.

13.1.2. Esquema de validadores

Un esquema de validadores es un validador especial que agrupa uno o varios validadores.

Si se produce un error, el esquema de validadores lanza una excepción de tipo

sfValidatorErrorSchema.

13.2. Validadores

A continuación se muestra el listado completo de todos los validadores de Symfony:

y  sfValidatorString y  sfValidatorRegex y  sfValidatorEmail y  sfValidatorUrl y  sfValidatorInteger y  sfValidatorNumber y  sfValidatorBoolean y  sfValidatorChoice y  sfValidatorPass y  sfValidatorCallback y  sfValidatorDate y  sfValidatorTime y  sfValidatorDateTime y  sfValidatorDateRange y  sfValidatorFile y  sfValidatorAnd y  sfValidatorOr 

Page 152: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 152/167

 

y  sfValidatorSchema y  sfValidatorSchemaCompare y  sfValidatorSchemaFilter y  sfValidatorI18nChoiceCountry y  sfValidatorI18nChoiceLanguage y  sfValidatorPropelChoice y  sfValidatorPropelChoiceMany y  sfValidatorPropelUnique y  sfValidatorDoctrineChoice y  sfValidatorDoctrineChoiceMany y  sfValidatorDoctrineUnique 

13.3. Validadores simples

13.3.1. sfValidatorString

sfValidatorString valida una cadena de texto y convierte el valor original en una cadenade texto.

Opción Error Descripción

max_length max_length La longitud máxima de la cadena de texto

min_length min_length La longitud mínima de la cadena de texto

Error Variables Valor por defecto (semuestra en inglés)

max_length max_length "%value%" is too long (%max_length% characters max).

min_length min_length "%value%" is too short (%min_length% characters min).

Cuidado 

Este validador requiere la extensión mb_string de PHP para funcionar correctamente. Si se

encuentra instalada, la longitud de la cadena de texto se calcula con la función

mb_strlen(). Si no se encuentra instalada, se emplea la función strlen(), que no calculacorrectamente la longitud de las cadenas de texto que utilizan caracteres que no seanASCII.

13.3.2. sfValidatorRegex

sfValidatorRegex valida una cadena de texto en función de la expresión regular indicada.

Opción Error Descripción

Page 153: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 153/167

 

pattern invalid Una expresión regular que siga el formato de PCRE

13.3.3. sfValidatorEmail

sfValidatorEmail valida que el valor indicado tenga el formato correcto de una direcciónde email. Este validador hereda de sfValidatorRegex.

13.3.4. sfValidatorUrl

sfValidatorEmail valida que el valor indicado tenga el formato correcto de una URL de

HTTP o FTP. Este validador hereda de sfValidatorRegex.

13.3.5. sfValidatorInteger

sfValidatorInteger valida un número entero y convierte el valor original en un número

entero.

Opción Error Descripción

max  max  El número entero más grande que se acepta como valor

min  min  El número entero más pequeño que se acepta como valor

Error Variables Valor por defecto (semuestra en inglés)

max  max  "%value%" must be less than %max%.

min  min  "%value%" must be greater than %min%.

El mensaje por defecto para el error de tipo invalid es "%value%" is not an integer. 

13.3.6. sfValidatorNumber

sfValidatorNumber valida un número y convierte el valor original en un número.Cualquier cadena de texto que PHP sea capaz de convertir mediante la función floatval se

considera un número.

Opción Error Descripción

max  max  El número más grande que se acepta como valor

min  min  El número más pequeño que se acepta como valor

Page 154: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 154/167

 

Error Variables Valor por defecto (semuestra en inglés)

max  max  "%value%" must be less than %max%.

min  min  "%value%" must be greater than %min%.

El mensaje por defecto para el error de tipo invalid es "%value%" is not a number..

13.3.7. sfValidatorBoolean

sfValidatorBoolean valida un valor booleano y devuelve true o false.

Opción Error Descripción

true_values  -

La lista de valores booleanos verdaderos (por defecto son true, t, yes, y,

on, 1)

false_values -La lista de valores booleanos falsos (por defecto son false, f, no, n, off,

0)

13.3.8. sfValidatorChoice

sfValidatorChoice valida que el valor original pertenezca a una la lista de valores

esperados.

Opción Error Descripción

choices  - Array con los valores esperados (esta opción es obligatoria)

multiple - true si la etiqueta <select> debe permitir selecciones múltiples

Nota 

La comparación se realiza después de convertir el valor original en una cadena de texto.

13.3.9. sfValidatorPass

sfValidatorPass es un validador especial que no realiza ninguna validación y quesimplemente devuelve el valor original intacto.

Page 155: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 155/167

 

13.3.10. sfValidatorCallback 

sfValidatorCallback permite delegar la validación del valor original al código PHP

ejecutable indicado, también llamado " callback " .

Al código PHP ejecutable se le pasan como argumentos la instancia del validador actual, elvalor original y un array de opciones (obtenido mediante la opción arguments):

function callback_validador_constante($validador, $valorOriginal,$argumentos){if($valorOriginal != $argumentos['constante']){

throw new sfValidatorError($validador, 'invalid');}

return$valorOriginal;}

$v = new sfValidatorCallback(array('callback' =>'callback_validador_constante','arguments' =>array('constante' =>'valor'),));

Opción Error Descripción

callback  - Un callback válido de PHP (obligatorio)

arguments - Array con las opciones que se le pasan al callback

13.4. Validadores de fechas

13.4.1. sfValidatorDate

sfValidatorDate valida fechas y fechas + horas (para utilizar fechas + horas, se debe

activar la opción with_time). Además de validar el formato de una fecha, puede forzar a

que la fecha sea anterior o posterior a una fecha indicada.

Este validador acepta como valor original diferentes tipos de variables:

y  Array con las siguientes claves: year, month, day, hour, minute y second y  Cadena de texto que cumpla opcionalmente con la expresión regular indicada en la opción

date_format 

y  Cadena de texto que se pueda procesar por la función strtotime() de PHP

y  Número entero que representa un timestamp

El valor original se convierte en una fecha aplicando el formato date_output odatetime_output 

Page 156: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 156/167

 

Opción Error Descripción

date_format  bad_format Expresión regular que deben cumplir las fechas

with_time  -true si el validador debe devolver también la hora,

false en cualquier otro caso

date_output  -El formato que se utiliza al devolver sólo una fecha

(por defecto es Y-m-d)

datetime_output  -El formato que se utiliza al devolver una fecha y una

hora (por defecto es Y-m-d H:i:s)

date_format_error  -

max  max La fecha máxima permitida (se indica como

timestamp)

min  min La fecha mínima permitida (se indica como

timestamp)

date_format_range_error -El formato de fecha que se utiliza al mostrar un error

de tipo max o min (por defecto es d/m/Y H:i:s)

Nota 

Las opciones date_output y datetime_output pueden emplear cualquier formato queentienda la función date() de PHP.

Error Variables Valor por defecto (semuestra en inglés)

bad_format date_format "%value%" does not match the date format (%date_format%).

min  min  The date must be after %min%.

max  max  The date must be before %max%.

13.4.2. sfValidatorTime

sfValidatorTime valida que el valor original sea una hora.

Este validador acepta como valor original diferentes tipos de variables:

y  Array con las siguientes claves: hour, minute y second 

Page 157: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 157/167

 

y  Cadena de texto que cumpla opcionalmente con la expresión regular indicada en la opcióntime_format 

y  Cadena de texto que se pueda procesar por la función strtotime() de PHP

y  Número entero que representa un timestamp

El valor original se convierte en una hora aplicando el formato date_output odatetime_output 

Opción Error Descripción

time_format  bad_format Expresión regular que deben cumplir las horas

time_output  -El formato que se utiliza al devolver una fecha con la hora

(por defecto es H:i:s)

time_format_error -

El formato de fecha que se utiliza al mostrar un error de tipo

bad_format. Si no se indica, se utiliza el formatodate_format 

Nota 

La opción time_output puede emplear cualquier formato que entienda la función date() 

de PHP.

Error Variables Valor por defecto (semuestra en inglés)

bad_format date_format "%value%" does not match the time format (%time_format%).

13.4.3. sfValidatorDateTime

sfValidatorDateTime valida las fechas que incluyen la hora. En realidad, este validador es un atajo del siguiente código:

$v = new sfValidatorDate(array('with_time' =>true));

13.4.4. sfValidatorDateRange

sfValidatorDateTime valida un rango de fechas.

Opción Error Descripción

from_date invalid El validador de la fecha inicial (obligatorio)

to_date  invalid El validador de la fecha final (obligatorio)

Page 158: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 158/167

 

Los validadores de from_date y to_date deben ser instancias de la clase

sfValidatorDate.

El mensaje por defecto del error de tipo invalid es "%value%" does not match thetime format (%time_format%). 

13.5. Validador de archivos

13.5.1. sfValidatorFile

sfValidatorFile valida los archivos subidos por los usuarios. Además, el validador 

convierte el archivo subido en una instancia de la clase sfValidatedFile o de la clase que

se indique en la opción validated_file_class.

Opción Error Descripción

max_size  max_size  El tamaño máximo permitido para los archivos subidos

mime_types  mime_types 

Array con los tipos MIME permitidos o el nombre de la

categoría de tipos MIME (la única categoría disponible es

web_images)

mime_type_guessers  -

Array con los ejecutables de PHP encargados de adivinar

el tipo MIME de los archivos (deben devolver o el tipo

MIME o null)

mime_categories  - Array de categorías de tipos MIME (la categoríaweb_images está definida por defecto)

path  -La ruta en la que se guarda el archivo y que utiliza la

clase sfValidatedFile (opcional)

validated_file_class -Nombre de la clase que gestiona el archivo subido y

validado (opcional)

La categoría web_images incluye los siguientes tipos MIME:

y  image/jpeg 

y  image/pjpeg 

y  image/png 

y  image/x-png 

y  image/gif 

Page 159: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 159/167

 

Si la opción mime_types está activada, el validador necesita comprobar el tipo MIME delarchivo subido. Por este motivo el validador ya incluye tres comprobadores de tipos

MIME:

y  guessFromFileinfo: utiliza la función finfo_open() (de la extensión Fileinfo de

PEC

L)y  guessFromMimeContentType: utiliza la función mime_content_type() (obsoleto)

y  guessFromFileBinary: utiliza el contenido del propio archivo (sólo funciona en los

sistemas *nix)

Error Variables Valor por defecto (semuestra en inglés)

max_size  %size%, %max_size%  File is too large (maximum is %max_size% bytes).

mime_types %mime_types%, %mime_type% Invalid mime type (%mime_type%).

partial  The uploaded file was only partially uploaded.

no_tmp_dir  Missing a temporary folder.

cant_write  Failed to write file to disk.

extension  File upload stopped by extension.

El validador asocia los errores de PHP de la siguiente manera:

y  UPLOAD_ERR_INI_SIZE: max_size y  UPLOAD_ERR_FORM_SIZE: max_size 

y  UPLOAD_ERR_PARTIAL: partial 

y  UPLOAD_ERR_NO_TMP_DIR: no_tmp_dir 

y  UPLOAD_ERR_CANT_WRITE: cant_write 

y  UPLOAD_ERR_EXTENSION: extension 

13.6. Validadores lógicos

13.6.1. sfValidatorAnd

sfValidatorAnd considera válido el valor original solamente si pasa correctamente una

lista de validadores.

El constructor de sfValidatorAnd toma como primer argumento una lista de validadores:

$v = new sfValidatorAnd(array(new sfValidatorString(array('max' =>255)),

Page 160: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 160/167

 

new sfValidatorEmail(),),array('halt_on_error' =>true),array('invalid' =>'El valor indicado debe ser un email de menos de 255caracteres de longitud.'));

Por defecto el validador guarda en un array todos los mensajes de error lanzados por los

validadores incluidos. También puede mostrar un único mensaje de error si se indica una

cadena de texto para el error de tipo invalid, tal y como se muestra en el ejemplo anterior.

Opción Error Descripción

halt_on_error -Indica si la validación se debe detener después del primer error o si debe

continuar (por defecto vale false)

El orden de los validadores es muy importante cuando se establece a true la opciónhalt_on_error.

La lista de validadores incluidos también se puede gestionar con los métodos

getValidators() y addValidator().

13.6.2. sfValidatorOr

sfValidatorOr considera válido el valor original si pasa correctamente al menos uno delos validadores indicados.

El constructor de sfValidatorOr toma como primer argumento una lista de validadores:

$v = new sfValidatorOr(array(new sfValidatorRegex(array('pattern' =>'/\.com$/')),new sfValidatorEmail(),),array(),array('invalid' =>'El valor indicado debe ser o un dominio .com o unadirección de email.'));

Por defecto el validador guarda en un array todos los mensajes de error lanzados por losvalidadores incluidos. También puede mostrar un único mensaje de error si se indica una

cadena de texto para el error de tipo invalid, tal y como se muestra en el ejemplo anterior.

La lista de validadores incluidos también se puede gestionar con los métodos

getValidators() y addValidator().

Page 161: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 161/167

 

13.6.3. sfValidatorSchema

*Schema validator *: Yes

sfValidatorSchema consiste en un validador compuesto por varios campos. Cada campo

se forma mediante un nombre y un validador:

$v = new sfValidatorSchema(array('nombre' =>new sfValidatorString(),'pais' =>new sfValidatorI18nChoiceCountry(),));

Nota 

Los formularios se definen mediante un esquema de validadores de la clase

sfValidatorSchema.

Este validador sólo acepta como argumento un array, por lo que lanza una excepción de

tipo InvalidArgumentException en cualquier otro caso.

Además, a este tipo de validador también se le puede asignar un pre-validador (que seejecuta antes que cualquier otro validador) y un post-validador (que se ejecuta sobre los

valores limpios después de todos los demás validadores).

Tanto el pre-validador como el post-validador, son esquemas de validadores a los que se les pasan todos los valores. Para establecer este tipo de validadores, se emplean los métodos

setPreValidator() y setPostValidator():

$v->setPostValidator(

new sfValidatorCompare('password1', '==', 'password2'));

Opción Error Descripción

allow_extra_fields  extra_fields 

Si vale false, el validador muestra un error cuando el

usuario envía más campos que los que tenía el

formulario original (por defecto vale false)

filter_extra_fields -

Si vale true, el validador elimina los campos

adicionales del array de valores limpios (por defecto

vale true)

Error Variables Valor por defecto (semuestra en inglés)

extra_fields  %field% Unexpected extra form field named "%field%".

post_max_size The form submission cannot be processed. It probably means that you

Page 162: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 162/167

 

have uploaded a file that is too big.

sfValidatorSchema permite acceder a sus validadores utilizando la notación de los arrays:

$vs = new sfValidatorSchema(array('nombre' =>new sfValidatorString()));

$validadorNombre = $vs['nombre'];

unset($vs['nombre']);

La notación de los arrays también se puede emplear para acceder a los esquemas de

validadores anidados:

$vs['autor']['nombre']->setMessage('invalid', 'El nombre no es válido.');

Si el tamaño de los datos enviados en el formulario excede del valor de la opción

post_max_size del archivo de configuración php.ini, se lanza un error de tipopost_max_size.

13.6.4. sfValidatorSchemaCompare

sfValidatorSchemaCompare permite comparar dos de los valores incluidos en el array devalores originales:

$v = new sfValidatorCompare('password1', '==', 'password2');

Opción Error Descripción

left_field  - El nombre del primer campo que se compara

operator  - El operador utilizado en la comparación

right_field  - El nombre del segundo campo que se compara

throw_global_error -Indica si se lanza un error global (por defecto vale false) o si se

lanza un error asociado con el primer campo que se compara

Los operadores disponibles para realizar comparaciones son los siguientes:

y  sfValidatorSchemaCompare::EQUAL o también == 

y  sfValidatorSchemaCompare::NOT_EQUAL o también != 

y  sfValidatorSchemaCompare::LESS_THAN o también < 

y  sfValidatorSchemaCompare::LESS_THAN_EQUAL o también <= 

y  sfValidatorSchemaCompare::GREATER_THAN o también > 

y  sfValidatorSchemaCompare::GREATER_THAN_EQUAL o también >= 

Page 163: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 163/167

 

Por defecto, el validador lanza un error de tipo global. Sin embargo, si la opción

throw_global_error vale true, se lanza un error relacionado con el primer campocomparado.

El mensaje del error de tipo invalid puede utilizar las siguientes variables: %left_field%,

%right_field% y %operator%.

13.6.5. sfValidatorSchemaFilter

sfValidatorSchemaFilter convierte un validador normal en un esquema de validadores.

En ocasiones es útil en el post-validador:

$v = new sfValidatorSchema();$v->setPostValidator(new sfValidatorSchemaFilter('email', new sfValidatorEmail()));

13.7. Validadores de internacionalización

13.7.1. sfValidatorI18nChoiceCountry

sfValidatorI18nChoiceCountry valida que el valor original sea un código de paísincluido en el estándar ISO 3166.

Opción Error Descripción

countries invalid Array con los códigos de país a utilizar (incluidos en el estándar ISO 3166)

13.7.2. sfValidatorI18nChoiceLanguage

sfValidatorI18nChoiceLanguage valida que el valor original sea un código de idiomaincluido en el estándar ISO 639-1.

Opción Error Descripción

languages invalid Array con los códigos de idioma a utilizar (incluidos en el estándar ISO 639-1)

13.8. Validadores de Propel

Page 164: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 164/167

 

13.8.1. sfValidatorPropelChoice

sfValidatorPropelChoice valida que el valor original se encuentre en la lista de registros

asociada a un modelo de Propel. Esta lista de registros se puede restringir utilizando la

opción criteria.

Por defecto se comprueba que el valor original sea la clave primaria del registro, pero se

 puede comprobar cualquier otra columna indicada mediante la opción column.

Opción Error Descripción

model  - La clase del modelo (esta opción es obligatoria)

criteria  - Objeto criteria utilizado para realizar la consulta

column  -

El nombre de la columna que se comprueba. Por defecto vale null, lo que

significa que se emplea la clave primaria. El nombre de la columna se indica

con el formato del nombre de los campos.

connection - La conexión Propel que se utiliza (por defecto valenull)

multiple  - Vale true si la lista desplegable permite selecciones múltiples

Nota 

Este validador no funciona con los modelos que utilizan claves primarias compuestas.

13.8.2. sfValidatorPropelChoiceMany

sfValidatorPropelChoiceMany valida que los valores originales se encuentren en la listade registros asociada a un modelo de Propel.

Nota 

Este validador utiliza como argumento un array, por lo que si se le pasa una cadena detexto, esta se convierte automáticamente en un array.

En realidad, este validador es un atajo de:

$v = new sfValidatorPropelChoice(array('multiple' =>true));

13.8.3. sfValidatorPropelUnique

sfValidatorPropelUnique valida que el valor de una columna o grupo de columnas

(mediante la opción column) sea único dentro de un modelo de Propel.

Page 165: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 165/167

 

Si se comprueban varias columnas, se puede lanzar un error global utilizando la opción

throw_global_error.

Opción Error Descripción

model  - La clase del modelo (esta opción es obligatoria)

column  -

El nombre de la columna cuyo valor debe ser único. El nombre se

indica siguiendo el formato de Propel para el nombre de los campos

(esta opción es obligatoria). Si se comprueba el valor de varias

columnas, se pasa un array con el nombre de los campos

field  -Nombre del campo que utiliza el formulario, distinto del nombre de

la columna

primary_key  -

El nombre de la columna de la clave primaria (es opcional, por lo

que si no se indica, Symfony intenta adivinarlo). También se puede

pasar un array si la tabla tiene varias claves primarias

connection  - La conexión Propel que se utiliza (por defecto valenull)

throw_global_error -Indica si se lanza un error global (por defecto vale false) o un

error asociado con el primer campo indicado en la opción column 

13.9. Validadores de Doctrine

13.9.1. sfValidatorDoctrineChoice

sfValidatorDoctrineChoice valida que el valor original se encuentre en la lista deregistros asociada a un modelo de Doctrine. Esta lista de registros se puede restringir 

utilizando la opción query.

Por defecto se comprueba que el valor original sea la clave primaria del registro, pero se

 puede comprobar cualquier otra columna indicada mediante la opción column.

Opción Error Descripción

model  - La clase del modelo (esta opción es obligatoria)

alias  - El alias del componente principal utilizado en la consulta

query  - Consulta que se utiliza para obtener los objetos

Page 166: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 166/167

 

column  -

El nombre de la columna que se comprueba. Por defecto vale null, lo que

significa que se emplea la clave primaria. El nombre de la columna se indica

con el formato del nombre de los campos.

connection - La conexión Propel que se utiliza (por defecto valenull)

Nota 

Este validador no funciona con los modelos que utilizan claves primarias compuestas.

13.9.2. sfValidatorDoctrineChoiceMany

sfValidatorDoctrineChoiceMany valida que los valores originales se encuentren en lalista de registros asociada a un modelo de Doctrine.

Nota 

Este validador utiliza como argumento un array, por lo que si se le pasa una cadena detexto, esta se convierte automáticamente en un array.

Este validador hereda todas las opciones del validador sfValidatorDoctrineChoice.

13.9.3. sfValidatorDoctrineUnique

sfValidatorDoctrineUnique valida que el valor de una columna o grupo de columnas

(mediante la opción column) sea único dentro de un modelo de Doctrine.

Si se comprueban varias columnas, se puede lanzar un error global utilizando la opción

throw_global_error.

Opción Error Descripción

model  - La clase del modelo (esta opción es obligatoria)

column  -

El nombre de la columna cuyo valor debe ser único. El nombre se

indica siguiendo el formato de Doctrine para el nombre de los

campos (esta opción es obligatoria). Si se comprueba el valor de

varias columnas, se pasa un array con el nombre de los campos

primary_key  -

El nombre de la columna de la clave primaria (es opcional, por lo

que si no se indica, Symfony intenta adivinarlo). También se puede

pasar un array si la tabla tiene varias claves primarias

connection  - La conexión Doctrine que se utiliza (por defecto valenull)

Page 167: Formularios Symphony

5/8/2018 Formularios Symphony - slidepdf.com

http://slidepdf.com/reader/full/formularios-symphony 167/167

 

throw_global_error -Indica si se lanza un error global (por defecto vale false) o un

error asociado con el primer campo indicado en la opción column