Los formularios de Symfony están formados por campos. En el capítulo 1, se explica cómo se 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 las reglas de validación a cada campo.

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étodo setValidators().

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

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

  public function 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:

  • sfValidatorString: valida una cadena de texto
  • sfValidatorEmail : valida un email
  • 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ón Valor por defecto Descripción
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
{
  public function 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()));
      }
    }
  }

  public function executeGracias()
  {
  }
}

El listado 2-3 incluye numerosos conceptos nuevos:

  • 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();
  • Cuando el usuario envía el formulario mediante una petición POST, 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'));

Una vez asociado, el formulario se puede validar mediante el método isValid(). 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()));
}

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.

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

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.

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 con muchos 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 el usuario 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 tipo sfValidatedFile, que facilita el acceso a la información del archivo. Más adelante en este capítulo se muestra cómo utilizar este validador.

Nota 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ían datos inválidos.

Formulario inválido

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(), el formulario pasa al estado asociado y se ejecutan las siguientes acciones:

  • Se ejecuta el proceso de validación
  • Los mensajes de error se almacenan en el formulario para que estén disponibles en la plantilla
  • 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.

Nota 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, estos valores 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'))