TigerZF
🌐Español

36.2. Inicio rápido de Zend_Form

Esta guía de inicio rápido cubre los fundamentos de la creación, validación y renderizado de formularios con Zend_Form.

36.2.1. Crear un objeto de formulario

Crear un objeto de formulario es muy sencillo: simplemente instancie Zend_Form:

$form = new Zend_Form;

Para casos de uso avanzados, quizá desee crear una subclase de Zend_Form, pero para formularios simples, puede crear un formulario de forma programática utilizando un objeto Zend_Form.

Si desea especificar la acción y el método del formulario (siempre es buena idea), puede hacerlo con los accesores setAction() y setMethod():

$form->setAction('/resource/process')
     ->setMethod('post');

El código anterior establece la acción del formulario a la URL parcial "/resource/process" y el método del formulario a HTTP POST. Esto se reflejará durante el renderizado final.

Puede establecer atributos HTML adicionales para la etiqueta <form> utilizando los métodos setAttrib() o setAttribs(). Por ejemplo, si desea establecer el id, establezca el atributo "id":

$form->setAttrib('id', 'login');

36.2.2. Añadir elementos al formulario

Un formulario no es nada sin sus elementos. Zend_Form incluye algunos elementos por defecto que se renderizan como XHTML mediante ayudantes (helpers) de Zend_View. Estos son los siguientes:

  • button

  • checkbox (o varios checkboxes a la vez con multiCheckbox)

  • hidden

  • image

  • password

  • radio

  • reset

  • select (tanto el tipo normal como el de selección múltiple)

  • submit

  • text

  • textarea

Tiene dos opciones para añadir elementos a un formulario: puede instanciar elementos concretos y pasar esos objetos, o puede pasar simplemente el tipo de elemento y dejar que Zend_Form instancie un objeto del tipo correcto por usted.

Algunos ejemplos:

// Instantiating an element and passing to the form object:
$form->addElement(new Zend_Form_Element_Text('username'));

// Passing a form element type to the form object:
$form->addElement('text', 'username');

Por defecto, estos no tienen ningún validador ni filtro. Esto significa que deberá configurar sus elementos con al menos validadores, y potencialmente filtros. Puede hacer esto (a) antes de pasar el elemento al formulario, (b) mediante opciones de configuración pasadas al crear un elemento a través de Zend_Form, o (c) obteniendo el elemento del objeto formulario y configurándolo después de eso.

Veamos primero cómo crear validadores para una instancia concreta de elemento. Puede pasar objetos Zend_Validate_*, o el nombre de un validador a utilizar:

$username = new Zend_Form_Element_Text('username');

// Passing a Zend_Validate_* object:
$username->addValidator(new Zend_Validate_Alnum());

// Passing a validator name:
$username->addValidator('alnum');

Al usar esta segunda opción, puede pasar argumentos del constructor en un array como el tercer parámetro si el validador puede aceptarlos:

// Pass a pattern
$username->addValidator('regex', false, array('/^[a-z]/i'));

(El segundo parámetro se utiliza para indicar si el fallo de este validador debe impedir o no que se ejecuten validadores posteriores; por defecto, es FALSE.)

También puede querer especificar un elemento como requerido. Esto puede hacerse mediante un accesor o pasando una opción al crear el elemento. En el primer caso:

// Make this element required:
$username->setRequired(true);

Cuando un elemento es requerido, se añade un validador 'NotEmpty' al principio de la cadena de validadores, asegurando que el elemento tenga un valor cuando sea requerido.

Los filtros se registran básicamente de la misma manera que los validadores. Con fines ilustrativos, añadamos un filtro para convertir a minúsculas el valor final:

$username->addFilter('StringtoLower');

La configuración final del elemento podría verse así:

$username->addValidator('alnum')
         ->addValidator('regex', false, array('/^[a-z]/'))
         ->setRequired(true)
         ->addFilter('StringToLower');

// or, more compactly:
$username->addValidators(array('alnum',
        array('regex', false, '/^[a-z]/i')
    ))
    ->setRequired(true)
    ->addFilters(array('StringToLower'));

Por simple que sea, repetir esto para cada elemento de un formulario puede resultar un poco tedioso. Probemos la opción (b) mencionada anteriormente. Cuando creamos un nuevo elemento usando Zend_Form::addElement() como fábrica, podemos pasar opcionalmente opciones de configuración. Estas pueden incluir validadores y filtros. Para hacer todo lo anterior de forma implícita, pruebe lo siguiente:

$form->addElement('text', 'username', array(
    'validators' => array(
        'alnum',
        array('regex', false, '/^[a-z]/i')
    ),
    'required' => true,
    'filters'  => array('StringToLower'),
));
[Note] Nota

Si observa que configura elementos usando las mismas opciones en muchos lugares, quizá quiera considerar crear su propia subclase de Zend_Form_Element y utilizar esa clase en su lugar; esto le ahorrará escritura a largo plazo.

36.2.3. Renderizar un formulario

Renderizar un formulario es sencillo. La mayoría de los elementos utilizan un ayudante (helper) de Zend_View para renderizarse a sí mismos, y por tanto necesitan un objeto de vista para renderizar. Aparte de eso, tiene dos opciones: usar el método render() del formulario, o simplemente hacer un echo de él.

// Explicitly calling render(), and passing an optional view object:
echo $form->render($view);

// Assuming a view object has been previously set via setView():
echo $form;

Por defecto, Zend_Form y Zend_Form_Element intentarán utilizar el objeto de vista inicializado en el ViewRenderer, lo que significa que no necesitará establecer la vista manualmente cuando use el MVC de Zend Framework. Para renderizar un formulario en una vista, simplemente debe hacer lo siguiente:

<?php echo $this->form ?>

Internamente, Zend_Form utiliza "decoradores" para llevar a cabo el renderizado. Estos decoradores pueden reemplazar contenido, añadir contenido al final, o añadir contenido al principio, y pueden inspeccionar completamente el elemento pasado a ellos. Como resultado, puede combinar múltiples decoradores para lograr efectos personalizados. Por defecto, Zend_Form_Element de hecho combina cuatro decoradores para lograr su salida; la configuración se ve algo así:

$element->addDecorators(array(
    'ViewHelper',
    'Errors',
    array('HtmlTag', array('tag' => 'dd')),
    array('Label', array('tag' => 'dt')),
));

(Donde <HELPERNAME> es el nombre de un ayudante de vista a utilizar, y varía según el elemento.)

Lo anterior crea una salida como la siguiente:

<dt><label for="username" class="required">Username</dt>
<dd>
    <input type="text" name="username" value="123-abc" />
    <ul class="errors">
        <li>'123-abc' has not only alphabetic and digit characters</li>
        <li>'123-abc' does not match against pattern '/^[a-z]/i'</li>
    </ul>
</dd>

(Aunque no con el mismo formato.)

Puede cambiar los decoradores utilizados por un elemento si desea obtener una salida diferente; consulte la sección sobre decoradores para más información.

El propio formulario simplemente recorre los elementos, y los envuelve en un HTML <form>. La acción y el método que proporcionó al configurar el formulario se pasan a la etiqueta <form>, al igual que cualquier atributo que haya establecido mediante setAttribs() y similares.

Los elementos se recorren en el orden en que fueron registrados, o, si su elemento contiene un atributo de orden, se utilizará ese orden. Puede establecer el orden de un elemento usando:

$element->setOrder(10);

O, al crear un elemento, pasándolo como opción:

$form->addElement('text', 'username', array('order' => 10));

36.2.4. Comprobar si un formulario es válido

Después de enviar un formulario, deberá comprobar si pasa las validaciones. Cada elemento se comprueba contra los datos proporcionados; si una clave que coincide con el nombre del elemento no está presente, y el elemento está marcado como requerido, las validaciones se ejecutan con un valor NULL.

¿De dónde vienen los datos? Puede usar $_POST o $_GET, o cualquier otra fuente de datos que tenga a mano (peticiones de servicios web, por ejemplo):

if ($form->isValid($_POST)) {
    // success!
} else {
    // failure!
}

Con peticiones AJAX, a veces puede permitirse validar un único elemento, o grupos de elementos. isValidPartial() validará un formulario parcial. A diferencia de isValid(), sin embargo, si una clave en particular no está presente, no ejecutará validaciones para ese elemento en particular:

if ($form->isValidPartial($_POST)) {
    // elements present all passed validations
} else {
    // one or more elements tested failed validations
}

Se puede usar un método adicional, processAjax(), para validar formularios parciales. A diferencia de isValidPartial(), devuelve una cadena en formato JSON que contiene mensajes de error en caso de fallo.

Suponiendo que sus validaciones han pasado, ahora puede obtener los valores filtrados:

$values = $form->getValues();

Si necesita los valores sin filtrar en algún momento, use:

$unfiltered = $form->getUnfilteredValues();

Si, por el contrario, necesita todos los valores válidos y filtrados de un formulario parcialmente válido, puede llamar a:

$values = $form->getValidValues($_POST);

36.2.5. Obtener el estado de error

¿Su formulario tuvo validaciones fallidas al enviarlo? En la mayoría de los casos, puede simplemente volver a renderizar el formulario, y los errores se mostrarán al usar los decoradores por defecto:

if (!$form->isValid($_POST)) {
    echo $form;

    // or assign to the view object and render a view...
    $this->view->form = $form;
    return $this->render('form');
}

Si desea inspeccionar los errores, dispone de dos métodos. getErrors() devuelve un array asociativo de nombres de elemento / códigos (donde codes es un array de códigos de error). getMessages() devuelve un array asociativo de nombres de elemento / mensajes (donde messages es un array asociativo de pares código de error / mensaje de error). Si un elemento dado no tiene ningún error, no se incluirá en el array.

36.2.6. Uniéndolo todo

Construyamos un formulario de acceso simple. Necesitará elementos que representen:

  • username

  • password

  • submit

Para nuestros propósitos, supongamos que un nombre de usuario válido debe contener solo caracteres alfanuméricos, comenzar con una letra, tener una longitud mínima de 6, y máxima de 20; se normalizarán a minúsculas. Las contraseñas deben tener un mínimo de 6 caracteres. Simplemente descartaremos el valor de submit cuando terminemos, para que pueda quedar sin validar.

Usaremos el poder de las opciones de configuración de Zend_Form para construir el formulario:

$form = new Zend_Form();
$form->setAction('/user/login')
     ->setMethod('post');

// Create and configure username element:
$username = $form->createElement('text', 'username');
$username->addValidator('alnum')
         ->addValidator('regex', false, array('/^[a-z]+/'))
         ->addValidator('stringLength', false, array(6, 20))
         ->setRequired(true)
         ->addFilter('StringToLower');

// Create and configure password element:
$password = $form->createElement('password', 'password');
$password->addValidator('StringLength', false, array(6))
         ->setRequired(true);

// Add elements to form:
$form->addElement($username)
     ->addElement($password)
     // use addElement() as a factory to create 'Login' button:
     ->addElement('submit', 'login', array('label' => 'Login'));

A continuación, crearemos un controlador para gestionar esto:

class UserController extends Zend_Controller_Action
{
    public function getForm()
    {
        // create form as above
        return $form;
    }

    public function indexAction()
    {
        // render user/form.phtml
        $this->view->form = $this->getForm();
        $this->render('form');
    }

    public function loginAction()
    {
        if (!$this->getRequest()->isPost()) {
            return $this->_forward('index');
        }
        $form = $this->getForm();
        if (!$form->isValid($_POST)) {
            // Failed validation; redisplay form
            $this->view->form = $form;
            return $this->render('form');
        }

        $values = $form->getValues();
        // now try and authenticate....
    }
}

Y un script de vista para mostrar el formulario:

<h2>Please login:</h2>
<?php echo $this->form ?>

Como notará en el código del controlador, aún hay más trabajo por hacer: aunque el envío pueda ser válido, es posible que todavía necesite realizar cierta autenticación usando Zend_Auth u otro mecanismo de autorización.

36.2.7. Usar un objeto Zend_Config

Todas las clases de Zend_Form son configurables mediante Zend_Config; puede pasar un objeto Zend_Config al constructor o pasarlo con setConfig(). Veamos cómo podríamos crear el formulario anterior utilizando un archivo INI. Primero, sigamos las recomendaciones, y coloquemos nuestras configuraciones en secciones que reflejen la ubicación de publicación, y centrémonos en la sección 'development'. A continuación, configuraremos una sección para el controlador dado ('user'), y una clave para el formulario ('login'):

[development]
; general form metainformation
user.login.action = "/user/login"
user.login.method = "post"

; username element
user.login.elements.username.type = "text"
user.login.elements.username.options.validators.alnum.validator = "alnum"
user.login.elements.username.options.validators.regex.validator = "regex"
user.login.elements.username.options.validators.regex.options.pattern = "/^[a-z]/i"
user.login.elements.username.options.validators.strlen.validator = "StringLength"
user.login.elements.username.options.validators.strlen.options.min = "6"
user.login.elements.username.options.validators.strlen.options.max = "20"
user.login.elements.username.options.required = true
user.login.elements.username.options.filters.lower.filter = "StringToLower"

; password element
user.login.elements.password.type = "password"
user.login.elements.password.options.validators.strlen.validator = "StringLength"
user.login.elements.password.options.validators.strlen.options.min = "6"
user.login.elements.password.options.required = true

; submit element
user.login.elements.submit.type = "submit"

Luego pasaría esto al constructor del formulario:

$config = new Zend_Config_Ini($configFile, 'development');
$form   = new Zend_Form($config->user->login);

y todo el formulario quedará definido.

36.2.8. Conclusión

Con suerte, gracias a este pequeño tutorial, ahora estará bien encaminado hacia aprovechar el poder y la flexibilidad de Zend_Form. ¡Siga leyendo para obtener información más detallada!