formularios symphony
TRANSCRIPT
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.
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(),
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.
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).
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
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:
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
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));
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.
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
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.
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''
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
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
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
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 "Mundo!""
class="<script>alert("hola")</script>"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.
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.
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.
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
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:
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.
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.
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.
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
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))),
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
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
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
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
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();
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(){// ...
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- ]+
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'));
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%")'))
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.
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
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);
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.
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).
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.
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.
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:
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
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:
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>
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.)
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.
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>
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')?>
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()?>
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
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
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á
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 ?>
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
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.
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.
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
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';}}
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.
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
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
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
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"> <a href="<?php echo url_for('autor/index') ?>">Cancel</a><?phpif(!$autor->isNew()): ?> <?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
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.
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.
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
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()
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.
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(){
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();
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
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.
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'))
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.
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.
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);
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{// ...
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(){// ...}}
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').
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.
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.
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
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',));
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);
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:
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
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(){
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'));
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
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.
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
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(),
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');
}}
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:
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'));
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
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>
5/8/2018 Formularios Symphony - slidepdf.com
http://slidepdf.com/reader/full/formularios-symphony 99/167
<tfoot><tr><td colspan="2"> <a href="<?php echo url_for('author/index') ?>">Cancel</a><?php if (!$author->isNew()): ?> <?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
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()
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().
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));}}
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
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{
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
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;}
// ...
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']);
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']);}}
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:
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);
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)
o
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
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
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());}}}
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);}}
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
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;}}
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():
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:
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()
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
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" />
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.
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:
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
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:
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%'),));
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
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
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>
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
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
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
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
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%'));
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:
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
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
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',));
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
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
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
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:
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)
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)
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:
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(),
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.
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.
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);
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.');
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
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
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
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.
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
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
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)
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
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)),
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().
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
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 >=
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
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.
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
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)
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