Symfony te permite personalizar cómo se muestra cada parte de los formulario. Puedes modificar la estructura de cada fila del formulario, la forma en la que se muestran los mensajes de error e incluso cómo debería renderizarse cada tipo de campo. No hay nada que no puedas modificar y puedes aplicar los cambios formulario por formulario, sin afectar a los demás formularios de la aplicación.
Symfony utiliza plantillas para renderizar todas y cada una de las partes de un formulario, como las etiquetas <label>
, las etiquetas <input>
, los mensajes de error y todo lo demás.
En las plantillas Twig, cada fragmento del formulario está representado por un bloque de Twig. Para personalizar alguna parte del formulario, sólo hay que reemplazar el bloque adecuado.
En las plantillas PHP, cada fragmento del formulario se renderiza mediante un archivo de plantilla. Para personalizar cualquier parte del formulario, sólo hay que reemplazar la plantilla existente creando una nueva.
Para entender mejor cómo funciona esto, vamos a personalizar el fragmento form_row
añadiendo un atributo class
al elemento div
que agrupa cada fila. Para ello, crea un nuevo archivo de plantilla con el siguiente contenido:
{# src/Acme/TaskBundle/Resources/views/Form/fields.html.twig #}
{% block form_row %}
{% spaceless %}
<div class="form_row">
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endspaceless %}
{% endblock form_row %}
<!-- src/Acme/TaskBundle/Resources/views/Form/form_row.html.php -->
<div class="form_row">
<?php echo $view['form']->label($form, $label) ?>
<?php echo $view['form']->errors($form) ?>
<?php echo $view['form']->widget($form, $parameters) ?>
</div>
El fragmento form_row
se usa cuando muestras los campos del formulario a través de la función form_row
. Para indicar al componente Form
que utilice este nuevo fragmento form_row
, añade lo siguiente en la parte superior de la plantilla que muestra el formulario:
{# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}
{% form_theme form 'AcmeTaskBundle:Form:fields.html.twig' %}
{% form_theme form 'AcmeTaskBundle:Form:fields.html.twig'
'AcmeTaskBundle:Form:fields2.html.twig' %}
<form ...>
<!-- src/Acme/TaskBundle/Resources/views/Default/new.html.php -->
<?php $view['form']->setTheme($form, array('AcmeTaskBundle:Form')) ?>
<?php $view['form']->setTheme($form, array('AcmeTaskBundle:Form', 'AcmeTaskBundle:Form')) ?>
<form ...>
La etiqueta form_theme
(en Twig) importa los fragmentos definidos en la plantilla indicada y los utiliza al mostrar el formulario. En otras palabras, cuando más adelante en esta plantilla se utilice la función form_row
, se empleará el bloque form_row
de tu tema en lugar del bloque form_row
predefinido de Symfony.
Los temas personalizados no están obligados a redefinir todos los bloques. Cuando la plantilla requiera un bloque que no se define en tu tema, se utiliza el bloque por defecto global de Symfony.
Si hay varios temas personalizados siempre se busca por orden en cada uno de ellos y si no existe en ninguno, se utiliza de nuevo el bloque definido globalmente.
Para personalizar cualquier parte de un formulario, sólo tienes que reemplazar el fragmento apropiado. La siguiente sección explica cómo saber exactamente qué bloque sustituir.
A partir de Symfony 2.1 existe una syntaxis alternativa de Twig para form_theme
(la diferencia más importante es que ahora puedes utilizar un array con los nombres de varios temas).
{# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}
{% form_theme form with 'AcmeTaskBundle:Form:fields.html.twig' %}
{% form_theme form with ['AcmeTaskBundle:Form:fields.html.twig', 'AcmeTaskBundle:Form:fields2.html.twig'] %}
12.10.1. Sintaxis de los fragmentos de formulario
En Symfony, cada parte de un formulario (elementos HTML, errores, títulos de campo, etc.) se definen en un tema base, que es una colección de bloques en Twig y una colección de plantillas en PHP.
En Twig, todos los bloques se definen en un solo archivo de plantilla llamado form_div_layout.html.twig
que se encuentra dentro del bridge de Twig. Si accedes a este archivo, verás todos los bloques que se utilizan para mostrar un formulario y cada uno de los tipos de campo.
En PHP, cada fragmento es una plantilla diferente. Todas las plantillas por defecto se encuentran en el directorio Resources/views/Form
del bundle FrameworkBundle
.
El nombre de cada fragmento sigue el mismo patrón básico y se divide en dos partes, separadas por un solo carácter de guión bajo (_
). Estos son algunos ejemplos:
form_row
, usado porform_row
para mostrar la mayoría de los campos.textarea_widget
, usado porform_widget
para mostrar un campo de tipotextarea
.form_errors
, usado porform_errors
para mostrar los errores de un campo.
Cada fragmento sigue el mismo patrón básico: type_part
. La porción type
corresponde al tipo del campo que se está mostrando (por ejemplo, textarea
, checkbox
, date
, etc.), mientras que la porción part
corresponde a lo que se está mostrando (por ejemplo, label
, widget
, errors
, etc.). Por defecto, hay cuatro posibles valores para el trozo part
:
Nombre | Ejemplo | Descripción |
---|---|---|
label |
form_label |
Muestra el título del campo |
widget |
form_widget |
Muestra las etiquetas HTML del campo |
errors |
form_errors |
Muestra los errores del campo |
row |
form_row |
Muestra el título, errores y etiquetas HTML del campo |
Nota En realidad, existen otras tres partes definidas (rows
, rest
y enctype
) pero es casi imposible que tengas que redefinir su contenido.
Si conoces el tipo de campo (por ejemplo, textarea
) y la parte que deseas personalizar (por ejemplo, widget
), puedes construir fácilmente el nombre del fragmento que debes crear (por ejemplo, textarea_widget
).
12.10.2. Herencia de fragmentos de plantilla
En algunos casos, parece que falta el fragmento que deseas personalizar.
Por ejemplo, no existe un fragmento llamado textarea_errors
en el tema definido por Symfony. Entonces, ¿cómo se muestran los errores de un campo textarea
?
La respuesta es: a través del fragmento form_errors
. Cuando Symfony renderiza los errores del tipo textarea
, primero busca un fragmento llamado textarea_errors
y si no existe, utiliza el fragmento form_errors
. Cada tipo de campo tiene un "tipo padre" (el tipo primario del textarea
es text
, y su padre es el form
), y Symfony utiliza el fragmento del padre si no existe el fragmento que busca.
Por lo tanto, para sustituir sólo los errores de los campos textarea
, copia el fragmento form_errors
, renómbralo como textarea_errors
y personalízalo. Para modificar cómo se muestran todos los errores, copia y personaliza el fragmento form_errors
directamente.
Truco El tipo padre de cada campo está documentado en la referencia para cada tipo de campo.
12.10.3. Temas globales de formulario
En el ejemplo anterior, se utiliza el helper form_theme
(en Twig) para importar fragmentos de formulario personalizados sólo para ese formulario. También puedes decirle a Symfony que aplique el mismo tema personalizado a todos los formularios de la aplicación.
12.10.3.1. Twig
Para incluir automáticamente en todas las plantillas los bloques personalizados del archivo fields.html.twig
definido anteriormente, modifica el archivo de configuración global de tu aplicación:
# app/config/config.yml
twig:
form:
resources:
- 'AcmeTaskBundle:Form:fields.html.twig'
# ...
<!-- app/config/config.xml -->
<twig:config ...>
<twig:form>
<resource>AcmeTaskBundle:Form:fields.html.twig</resource>
</twig:form>
<!-- ... -->
</twig:config>
// app/config/config.php
$container->loadFromExtension('twig', array(
'form' => array(
'resources' => array(
'AcmeTaskBundle:Form:fields.html.twig',
),
),
// ...
));
Con esta configuración, cualquier bloque que definas en el archivo fields.html.twig
se utilizará en todos los formularios de la aplicación.
12.10.3.2. PHP
Para incluir automáticamente todas las plantillas personalizadas del directorio Acme/TaskBundle/Resources/views/Form
creado anteriormente, modifica el archivo de configuración global de tu aplicación:
# app/config/config.yml
framework:
templating:
form:
resources:
- 'AcmeTaskBundle:Form'
# ...
<!-- app/config/config.xml -->
<framework:config ...>
<framework:templating>
<framework:form>
<resource>AcmeTaskBundle:Form</resource>
</framework:form>
</framework:templating>
<!-- ... -->
</framework:config>
// app/config/config.php
$container->loadFromExtension('framework', array(
'templating' => array('form' =>
array('resources' => array(
'AcmeTaskBundle:Form',
)))
// ...
));
Con esta configuración, cualquier fragmento dentro del directorio Acme/TaskBundle/Resources/views/Form
se aplicará automáticamente a todos los formularios de la aplicación.