Los formularios de Symfony 1.4

3.4. Personalizando el diseño

Más allá de la personalización global que permite el método render(), a continuación se explica 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 renderRow()

<form action="<?php echo url_for('contacto/index') ?>" method="POST">
  <table>
    <?php echo $formulario['nombre']->renderRow() ?>
    <?php echo $formulario['email']->renderRow() ?>
    <?php echo $formulario['asunto']->renderRow() ?>
    <?php echo $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.

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 el nombre 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 renderRow() para personalizar el aspecto del formulario

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

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():

  • 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:

  • array() significa que no se quieren añadir atributos HTML propios a la etiqueta <textarea> generada.
  • "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.

Estructura HTML utilizada por renderRow() y render()

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

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

  • renderLabel(): genera el título (etiqueta <label> asociada al campo)
  • render(): genera el campo (etiqueta <input>, <select>, <textarea>, etc.)
  • renderError(): genera los mensajes de error (en forma de lista de elementos <ul class="error_list">)
Métodos disponibles para personalizar un campo

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.

Mostrando un formulario en varias columnas

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.

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() sobre el 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>

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 render()

<?php echo $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 cada campo.

Listado 3-9 - Utilizando el método renderLabel()

<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

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

// 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 te aseguras que el campo sea accesible ya que cuando se pulsa sobre la etiqueta, el campo se selecciona de forma automática:

<label for="contacto_email">Email</label>
<input type="text" nombre="contacto[email]" id="contacto_email" />

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

<?php echo $formulario['enviar_notificacion']->renderLabel(null, array('class' => 'inline')) ?>

// Código HTML generado
<label for="contacto_enviar_notificacion" class="inline">Enviar notificacion</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 renderError()

<form action="<?php echo url_for('contacto/index') ?>" method="POST">
  <table>
    <tr>
      <th><?php echo $formulario['nombre']->renderLabel() ?>:</th>
      <td>
        <?php echo $formulario['nombre']->renderError() ?>
        <?php echo $formulario['nombre'] ?>
      </td>
      <th><?php echo $formulario['email']->renderLabel() ?>:</th>
      <td>
        <?php echo $formulario['email']->renderError() ?>
        <?php echo $formulario['email'] ?>
      </td>
    </tr>
    <tr>
      <th><?php echo $formulario['asunto']->renderLabel() ?>:</th>
      <td colspan="3">
        <?php echo $formulario['asunto']->renderError() ?>
        <?php echo $formulario['asunto'] ?>
      </td>
    </tr>
    <tr>
      <th><?php echo $formulario['mensaje']->renderLabel() ?>:</th>
      <td colspan="3">
        <?php echo $formulario['mensaje']->renderError() ?>
        <?php echo $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

<?php if ($formulario['email']->hasError()): ?>
  <ul class="error_list">
    <?php foreach ($formulario['email']->getError() as $error): ?>
      <li><?php echo $error ?></li>
    <?php endforeach; ?>
  </ul>
<?php endif; ?>

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ódigo HTML 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

<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">. Obviamente, 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 se utilizan 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 esos errores.

Mostrando los mensajes de error globales

Figura 3.8 Mostrando los mensajes de error globales

Listado 3-14 - Generando mensajes de error globales

<tr>
  <td colspan="2">
    <ul class="error_list">
      <li>Origen: Required.</li>
    </ul>
  </td>
</tr>

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

3.4.7. Trabajando con los errores globales

Los formularios disponen de tres tipos de error:

  • Errores asociados a un campo específico
  • Errores globales
  • 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 y el 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">
        <?php echo $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 los mé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()

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

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á 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ó este capítulo, el diseño de la nueva plantilla es completamente personalizable.

Formulario personalizado que utiliza los métodos de los campos

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