TigerZF
🌐Español

36.4. Creación de formularios usando Zend_Form

La clase Zend_Form se utiliza para agrupar elementos de formulario, grupos de visualización y subformularios. A continuación puede realizar las siguientes acciones sobre esos elementos:

  • Validación, incluyendo la recuperación de códigos y mensajes de error

  • Agregación de valores, incluyendo poblar elementos y recuperar tanto valores filtrados como sin filtrar de todos los elementos

  • Iteración sobre todos los elementos, en el orden en que fueron introducidos o en base a las indicaciones de orden recuperadas de cada elemento

  • Renderizado de todo el formulario, ya sea mediante un único decorador que realiza un renderizado personalizado o iterando sobre cada elemento del formulario

Aunque los formularios creados con Zend_Form pueden ser complejos, probablemente su mejor caso de uso son los formularios sencillos; su mejor uso está en el desarrollo rápido de aplicaciones (RAD) y la creación de prototipos.

En su forma más básica, simplemente se instancia un objeto de formulario:

// Generic form object:
$form = new Zend_Form();

// Custom form object:
$form = new My_Form()

Opcionalmente se puede pasar una instancia de Zend_Config o un array, que se usará para establecer el estado del objeto y potencialmente crear nuevos elementos:

// Passing in configuration options:
$form = new Zend_Form($config);

Zend_Form es iterable, e iterará sobre elementos, grupos de visualización y subformularios, usando el orden en que fueron registrados y cualquier índice de orden que cada uno pueda tener. Esto resulta útil en los casos en los que se desea renderizar los elementos manualmente en el orden apropiado.

La magia de Zend_Form reside en su capacidad para actuar como una fábrica de elementos y grupos de visualización, así como en su capacidad de renderizarse a sí mismo mediante decoradores.

36.4.1. Cargadores de plugins

Zend_Form hace uso de Zend_Loader_PluginLoader para permitir a los desarrolladores especificar las ubicaciones de elementos y decoradores alternativos. Cada uno tiene su propio cargador de plugins asociado, y se utilizan métodos de acceso generales para recuperarlos y modificarlos.

Los siguientes tipos de cargador se usan con los diversos métodos del cargador de plugins: 'element' y 'decorator'. Los nombres de tipo no distinguen mayúsculas de minúsculas.

Los métodos usados para interactuar con los cargadores de plugins son los siguientes:

  • setPluginLoader($loader, $type): $loader es el propio objeto cargador de plugins, mientras que type es uno de los tipos especificados arriba. Esto establece el cargador de plugins para el tipo indicado con el nuevo objeto cargador especificado.

  • getPluginLoader($type): recupera el cargador de plugins asociado con $type.

  • addPrefixPath($prefix, $path, $type = null): añade una asociación de prefijo/ruta al cargador especificado por $type. Si $type es NULL, se intentará añadir la ruta a todos los cargadores, añadiendo al prefijo "_Element" y "_Decorator"; y añadiendo a la ruta "Element/" y "Decorator/". Si tiene todas sus clases adicionales de elementos de formulario bajo una jerarquía común, este es un método conveniente para establecer el prefijo base de todas ellas.

  • addPrefixPaths(array $spec): permite añadir muchas rutas a la vez a uno o más cargadores de plugins. Espera que cada elemento del array sea un array con las claves 'path', 'prefix' y 'type'.

Adicionalmente, se pueden especificar rutas de prefijo para todos los elementos y grupos de visualización creados a través de una instancia de Zend_Form usando los siguientes métodos:

  • addElementPrefixPath($prefix, $path, $type = null): Igual que addPrefixPath(), debe especificar un prefijo de clase y una ruta. $type, cuando se especifica, debe ser uno de los tipos de cargador de plugins especificados por Zend_Form_Element; consulte la sección de plugins de elementos para más información sobre los valores válidos de $type. Si no se especifica $type, el método asumirá que se trata de un prefijo general para todos los tipos.

  • addDisplayGroupPrefixPath($prefix, $path): Igual que addPrefixPath(), debe especificar un prefijo de clase y una ruta; sin embargo, dado que los grupos de visualización solo admiten decoradores como plugins, no se necesita ningún $type.

Los elementos y decoradores personalizados son una forma sencilla de compartir funcionalidad entre formularios y encapsular funcionalidad personalizada. Consulte el ejemplo de etiqueta personalizada en la documentación de elementos para ver un ejemplo de cómo los elementos personalizados pueden usarse como reemplazo de las clases estándar.

36.4.2. Elementos

Zend_Form proporciona varios métodos de acceso para añadir y eliminar elementos de formulario de un formulario. Estos pueden aceptar instancias de objetos elemento o servir como fábricas para instanciar los propios objetos elemento.

El método más básico para añadir un elemento es addElement(). Este método puede aceptar un objeto de tipo Zend_Form_Element (o de una clase que extienda Zend_Form_Element), o argumentos para construir un nuevo elemento, incluyendo el tipo de elemento, el nombre y cualquier opción de configuración.

Algunos ejemplos:

// Using an element instance:
$element = new Zend_Form_Element_Text('foo');
$form->addElement($element);

// Using a factory
//
// Creates an element of type Zend_Form_Element_Text with the
// name of 'foo':
$form->addElement('text', 'foo');

// Pass label option to the element:
$form->addElement('text', 'foo', array('label' => 'Foo:'));
[Note] addElement() implementa una interfaz fluida

addElement() implementa una interfaz fluida; es decir, devuelve el objeto Zend_Form, y no el elemento. Esto se hace para permitir encadenar varios métodos addElement() u otros métodos del formulario que implementen la interfaz fluida (todos los setters de Zend_Form implementan este patrón).

Si se desea que se devuelva el elemento en su lugar, use createElement(), que se describe a continuación. Tenga en cuenta, sin embargo, que createElement() no adjunta el elemento al formulario.

Internamente, addElement() en realidad usa createElement() para crear el elemento antes de adjuntarlo al formulario.

Una vez que un elemento se ha añadido al formulario, se puede recuperar por nombre. Esto puede hacerse ya sea usando el método getElement() o usando sobrecarga para acceder al elemento como una propiedad de objeto:

// getElement():
$foo = $form->getElement('foo');

// As object property:
$foo = $form->foo;

Ocasionalmente, puede que desee crear un elemento sin adjuntarlo al formulario (por ejemplo, si desea hacer uso de las diversas rutas de plugin registradas con el formulario, pero desea adjuntar más tarde el objeto a un subformulario). El método createElement() permite hacerlo:

// $username becomes a Zend_Form_Element_Text object:
$username = $form->createElement('text', 'username');

36.4.2.1. Poblado y recuperación de valores

Después de validar un formulario, normalmente necesitará recuperar los valores para poder realizar otras operaciones, como actualizar una base de datos o notificar a un servicio web. Puede recuperar todos los valores para todos los elementos usando getValues(); getValue($name) permite recuperar el valor de un único elemento por su nombre:

// Get all values:
$values = $form->getValues();

// Get only 'foo' element's value:
$value = $form->getValue('foo');

A veces querrá poblar el formulario con valores especificados antes de renderizarlo. Esto puede hacerse con los métodos setDefaults() o populate() :

$form->setDefaults($data);
$form->populate($data);

Por otro lado, puede que desee limpiar un formulario después de poblarlo o validarlo; esto puede hacerse usando el método reset():

$form->reset();

36.4.2.2. Operaciones globales

En ocasiones querrá que ciertas operaciones afecten a todos los elementos. Los escenarios comunes incluyen la necesidad de establecer rutas de prefijo de plugin para todos los elementos, establecer decoradores para todos los elementos y establecer filtros para todos los elementos. Como ejemplos:

Ejemplo 36.2. Establecer rutas de prefijo para todos los elementos

Puede establecer rutas de prefijo para todos los elementos por tipo, o usando un prefijo global. Algunos ejemplos:

// Set global prefix path:
// Creates paths for prefixes My_Foo_Filter, My_Foo_Validate,
// and My_Foo_Decorator
$form->addElementPrefixPath('My_Foo', 'My/Foo/');

// Just filter paths:
$form->addElementPrefixPath('My_Foo_Filter',
                            'My/Foo/Filter',
                            'filter');

// Just validator paths:
$form->addElementPrefixPath('My_Foo_Validate',
                            'My/Foo/Validate',
                            'validate');

// Just decorator paths:
$form->addElementPrefixPath('My_Foo_Decorator',
                            'My/Foo/Decorator',
                            'decorator');

Ejemplo 36.3. Establecer decoradores para todos los elementos

Puede establecer decoradores para todos los elementos. setElementDecorators() acepta un array de decoradores, igual que setDecorators(), y sobrescribirá cualquier decorador previamente establecido en cada elemento. En este ejemplo, establecemos los decoradores simplemente como un ViewHelper y un Label:

$form->setElementDecorators(array(
    'ViewHelper',
    'Label'
));

Ejemplo 36.4. Establecer decoradores para algunos elementos

También puede establecer decoradores para un subconjunto de elementos, ya sea por inclusión o exclusión. El segundo argumento de setElementDecorators() puede ser un array de nombres de elementos; por defecto, especificar tal array establecerá los decoradores indicados solo en esos elementos. También puede pasar un tercer argumento, una marca que indica si esta lista de elementos es de inclusión o exclusión. Si la marca es FALSE, decorará todos los elementos excepto los de la lista pasada. Como en el uso estándar del método, cualquier decorador que se pase sobrescribirá los decoradores previamente establecidos en cada elemento.

En el siguiente fragmento, indicamos que solo queremos los decoradores ViewHelper y Label para los elementos 'foo' y 'bar':

$form->setElementDecorators(
    array(
        'ViewHelper',
        'Label'
    ),
    array(
        'foo',
        'bar'
    )
);

Por otro lado, con este fragmento, ahora indicaremos que queremos usar únicamente los decoradores ViewHelper y Label para todos los elementos excepto los elementos 'foo' y 'bar':

$form->setElementDecorators(
    array(
        'ViewHelper',
        'Label'
    ),
    array(
        'foo',
        'bar'
    ),
    false
);

[Note] Algunos decoradores son inadecuados para algunos elementos

Aunque setElementDecorators() puede parecer una buena solución, hay algunos casos en los que puede terminar dando resultados inesperados. Por ejemplo, los diversos elementos de botón (Submit, Button, Reset) actualmente usan la etiqueta como el valor del botón, y solo usan los decoradores ViewHelper y DtDdWrapper, evitando que se renderice una etiqueta, errores y sugerencias adicionales. El ejemplo anterior duplicaría algún contenido (la etiqueta) para los elementos de botón.

Puede usar el array de inclusión/exclusión para superar este problema, como se indicó en el ejemplo anterior.

Por lo tanto, use este método con prudencia, y tenga en cuenta que puede necesitar excluir algunos elementos o cambiar manualmente los decoradores de algunos elementos para evitar una salida no deseada.

Ejemplo 36.5. Establecer filtros para todos los elementos

En algunos casos, puede que desee aplicar el mismo filtro a todos los elementos; un caso común es aplicar trim() a todos los valores:

$form->setElementFilters(array('StringTrim'));

36.4.2.3. Métodos para interactuar con elementos

Los siguientes métodos pueden usarse para interactuar con elementos:

  • createElement($element, $name = null, $options = null)

  • addElement($element, $name = null, $options = null)

  • addElements(array $elements)

  • setElements(array $elements)

  • getElement($name)

  • getElements()

  • removeElement($name)

  • clearElements()

  • setDefaults(array $defaults)

  • setDefault($name, $value)

  • getValue($name)

  • getValues()

  • getUnfilteredValue($name)

  • getUnfilteredValues()

  • setElementFilters(array $filters)

  • setElementDecorators(array $decorators)

  • addElementPrefixPath($prefix, $path, $type = null)

  • addElementPrefixPaths(array $spec)

36.4.3. Grupos de visualización

Los grupos de visualización son una forma de crear agrupaciones virtuales de elementos con fines de visualización. Todos los elementos permanecen accesibles por nombre en el formulario, pero al iterar sobre el formulario o renderizarlo, cualquier elemento en un grupo de visualización se renderiza en conjunto. El caso de uso más común para esto es la agrupación de elementos en fieldsets.

La clase base para los grupos de visualización es Zend_Form_DisplayGroup. Aunque puede instanciarse directamente, generalmente es mejor usar el método addDisplayGroup() de Zend_Form para hacerlo. Este método toma un array de nombres de elementos como primer argumento, y un nombre para el grupo de visualización como segundo argumento. Opcionalmente se puede pasar un array de opciones o un objeto Zend_Config como tercer argumento.

Suponiendo que los elementos 'username' y 'password' ya están establecidos en el formulario, el siguiente código agruparía estos elementos en un grupo de visualización 'login':

$form->addDisplayGroup(array('username', 'password'), 'login');

Puede acceder a los grupos de visualización usando el método getDisplayGroup(), o mediante sobrecarga usando el nombre del grupo de visualización:

// Using getDisplayGroup():
$login = $form->getDisplayGroup('login');

// Using overloading:
$login = $form->login;
[Note] No es necesario cargar los decoradores por defecto

Por defecto, los decoradores por defecto se cargan durante la inicialización del objeto. Puede deshabilitar esto pasando la opción 'disableLoadDefaultDecorators' al crear un grupo de visualización:

$form->addDisplayGroup(
    array('foo', 'bar'),
    'foobar',
    array('disableLoadDefaultDecorators' => true)
);

Esta opción puede combinarse con cualquier otra opción que se pase, tanto como opciones de array como en un objeto Zend_Config.

36.4.3.1. Operaciones globales

Al igual que con los elementos, hay algunas operaciones que podrían afectar a todos los grupos de visualización; estas incluyen establecer decoradores y establecer la ruta de plugin en la que buscar decoradores.

Ejemplo 36.6. Establecer la ruta de prefijo de decorador para todos los grupos de visualización

Por defecto, los grupos de visualización heredan cualquiera que sea la ruta de decorador que use el formulario; sin embargo, si deben buscar en ubicaciones alternativas, puede usar el método addDisplayGroupPrefixPath().

$form->addDisplayGroupPrefixPath('My_Foo_Decorator', 'My/Foo/Decorator');

Ejemplo 36.7. Establecer decoradores para todos los grupos de visualización

Puede establecer decoradores para todos los grupos de visualización. setDisplayGroupDecorators() acepta un array de decoradores, igual que setDecorators(), y sobrescribirá cualquier decorador previamente establecido en cada grupo de visualización. En este ejemplo, establecemos los decoradores simplemente como un fieldset (el decorador FormElements es necesario para asegurar que los elementos se iteren):

$form->setDisplayGroupDecorators(array(
    'FormElements',
    'Fieldset'
));

36.4.3.2. Uso de clases de grupo de visualización personalizadas

Por defecto, Zend_Form usa la clase Zend_Form_DisplayGroup para los grupos de visualización. Puede que necesite extender esta clase para proporcionar funcionalidad personalizada. addDisplayGroup() no permite pasar una instancia concreta, pero sí permite especificar la clase a usar como una de sus opciones, usando la clave 'displayGroupClass':

// Use the 'My_DisplayGroup' class
$form->addDisplayGroup(
    array('username', 'password'),
    'user',
    array('displayGroupClass' => 'My_DisplayGroup')
);

Si la clase aún no se ha cargado, Zend_Form intentará hacerlo usando Zend_Loader.

También puede especificar una clase de grupo de visualización por defecto para usar con el formulario de manera que todos los grupos de visualización creados con ese objeto de formulario usen dicha clase:

// Use the 'My_DisplayGroup' class for all display groups:
$form->setDefaultDisplayGroupClass('My_DisplayGroup');

Esta configuración puede especificarse en configuraciones como 'defaultDisplayGroupClass', y se cargará tempranamente para asegurar que todos los grupos de visualización usen esa clase.

36.4.3.3. Métodos para interactuar con grupos de visualización

Los siguientes métodos pueden usarse para interactuar con los grupos de visualización:

  • addDisplayGroup(array $elements, $name, $options = null)

  • addDisplayGroups(array $groups)

  • setDisplayGroups(array $groups)

  • getDisplayGroup($name)

  • getDisplayGroups()

  • removeDisplayGroup($name)

  • clearDisplayGroups()

  • setDisplayGroupDecorators(array $decorators)

  • addDisplayGroupPrefixPath($prefix, $path)

  • setDefaultDisplayGroupClass($class)

  • getDefaultDisplayGroupClass($class)

36.4.3.4. Métodos de Zend_Form_DisplayGroup

Zend_Form_DisplayGroup tiene los siguientes métodos, agrupados por tipo:

  • Configuración:

    • setOptions(array $options)

    • setConfig(Zend_Config $config)

  • Metadatos:

    • setAttrib($key, $value)

    • addAttribs(array $attribs)

    • setAttribs(array $attribs)

    • getAttrib($key)

    • getAttribs()

    • removeAttrib($key)

    • clearAttribs()

    • setName($name)

    • getName()

    • setDescription($value)

    • getDescription()

    • setLegend($legend)

    • getLegend()

    • setOrder($order)

    • getOrder()

  • Elementos:

    • createElement($type, $name, array $options = array())

    • addElement($typeOrElement, $name, array $options = array())

    • addElements(array $elements)

    • setElements(array $elements)

    • getElement($name)

    • getElements()

    • removeElement($name)

    • clearElements()

  • Cargadores de plugins:

    • setPluginLoader(Zend_Loader_PluginLoader $loader)

    • getPluginLoader()

    • addPrefixPath($prefix, $path)

    • addPrefixPaths(array $spec)

  • Decoradores:

    • addDecorator($decorator, $options = null)

    • addDecorators(array $decorators)

    • setDecorators(array $decorators)

    • getDecorator($name)

    • getDecorators()

    • removeDecorator($name)

    • clearDecorators()

  • Renderizado:

    • setView(Zend_View_Interface $view = null)

    • getView()

    • render(Zend_View_Interface $view = null)

  • I18n:

    • setTranslator(Zend_Translate_Adapter $translator = null)

    • getTranslator()

    • setDisableTranslator($flag)

    • translatorIsDisabled()

36.4.4. Subformularios

Los subformularios sirven para varios propósitos:

  • Crear agrupaciones lógicas de elementos. Dado que los subformularios son simplemente formularios, se pueden validar subformularios como entidades individuales.

  • Crear formularios de varias páginas. Dado que los subformularios son simplemente formularios, se puede mostrar un subformulario distinto por página, construyendo formularios de varias páginas donde cada formulario tiene su propia lógica de validación. Solo cuando todos los subformularios validen se considerará completo el formulario.

  • Agrupaciones de visualización. Al igual que los grupos de visualización, los subformularios, cuando se renderizan como parte de un formulario más grande, pueden usarse para agrupar elementos. Sin embargo, tenga en cuenta que el objeto de formulario maestro no tendrá conocimiento de los elementos de los subformularios.

Un subformulario puede ser un objeto Zend_Form, o, más típicamente, un objeto Zend_Form_SubForm. Este último contiene decoradores adecuados para su inclusión en un formulario más grande (es decir, no renderiza etiquetas de formulario HTML adicionales, pero sí agrupa elementos). Para adjuntar un subformulario, simplemente añádalo al formulario y déle un nombre:

$form->addSubForm($subForm, 'subform');

Puede recuperar un subformulario usando ya sea getSubForm($name) o mediante sobrecarga usando el nombre del subformulario:

// Using getSubForm():
$subForm = $form->getSubForm('subform');

// Using overloading:
$subForm = $form->subform;

Los subformularios se incluyen en la iteración del formulario, aunque los elementos que contienen no lo están.

36.4.4.1. Operaciones globales

Al igual que los elementos y los grupos de visualización, hay algunas operaciones que podrían necesitar afectar a todos los subformularios. A diferencia de los grupos de visualización y los elementos, sin embargo, los subformularios heredan la mayor parte de su funcionalidad del objeto de formulario maestro, y la única operación real que podría necesitar realizarse globalmente es establecer decoradores para los subformularios. Con este propósito, existe el método setSubFormDecorators() . En el siguiente ejemplo, estableceremos el decorador para todos los subformularios simplemente como un fieldset (el decorador FormElements es necesario para asegurar que sus elementos se iteren):

$form->setSubFormDecorators(array(
    'FormElements',
    'Fieldset'
));

36.4.4.2. Métodos para interactuar con subformularios

Los siguientes métodos pueden usarse para interactuar con subformularios:

  • addSubForm(Zend_Form $form, $name, $order = null)

  • addSubForms(array $subForms)

  • setSubForms(array $subForms)

  • getSubForm($name)

  • getSubForms()

  • removeSubForm($name)

  • clearSubForms()

  • setSubFormDecorators(array $decorators)

36.4.5. Metadatos y atributos

Aunque la utilidad de un formulario deriva principalmente de los elementos que contiene, también puede contener otros metadatos, como un nombre (usado a menudo como un ID único en el marcado HTML); la acción y el método del formulario; el número de elementos, grupos y subformularios que contiene; y metadatos arbitrarios (usados normalmente para establecer atributos HTML para la propia etiqueta del formulario).

Puede establecer y recuperar el nombre de un formulario usando los métodos de acceso al nombre:

// Set the name:
$form->setName('registration');

// Retrieve the name:
$name = $form->getName();

Para establecer la acción (url a la que se envía el formulario) y el método (método mediante el cual debe enviarse, por ejemplo, 'POST' o 'GET'), use los métodos de acceso action y method:

// Set the action and method:
$form->setAction('/user/login')
     ->setMethod('post');

También puede especificar el tipo de codificación del formulario específicamente usando los métodos de acceso enctype. Zend_Form define dos constantes, Zend_Form::ENCTYPE_URLENCODED y Zend_Form::ENCTYPE_MULTIPART, que corresponden a los valores 'application/x-www-form-urlencoded' y 'multipart/form-data', respectivamente; sin embargo, puede establecer esto a cualquier tipo de codificación arbitrario.

// Set the action, method, and enctype:
$form->setAction('/user/login')
     ->setMethod('post')
     ->setEnctype(Zend_Form::ENCTYPE_MULTIPART);
[Note] Nota

El método, la acción y el enctype solo se usan internamente para el renderizado, y no para ningún tipo de validación.

Zend_Form implementa la interfaz Countable , lo que permite pasarlo como argumento a count:

$numItems = count($form);

El establecimiento de metadatos arbitrarios se realiza mediante los métodos de acceso attribs. Dado que la sobrecarga en Zend_Form se usa para acceder a elementos, grupos de visualización y subformularios, este es el único método para acceder a los metadatos.

// Setting attributes:
$form->setAttrib('class', 'zend-form')
     ->addAttribs(array(
         'id'       => 'registration',
         'onSubmit' => 'validate(this)',
     ));

// Retrieving attributes:
$class = $form->getAttrib('class');
$attribs = $form->getAttribs();

// Remove an attribute:
$form->removeAttrib('onSubmit');

// Clear all attributes:
$form->clearAttribs();

36.4.6. Decoradores

Crear el marcado para un formulario suele ser una tarea que consume tiempo, particularmente si se planea reutilizar el mismo marcado para mostrar cosas tales como errores de validación, valores enviados, etc. La respuesta de Zend_Form a este problema son los decoradores.

Los decoradores para objetos Zend_Form pueden usarse para renderizar un formulario. El decorador FormElements iterará sobre todos los elementos de un formulario (elementos, grupos de visualización y subformularios) y los renderizará, devolviendo el resultado. Luego se pueden usar decoradores adicionales para envolver este contenido, o añadirlo al principio o al final.

Los decoradores por defecto para Zend_Form son FormElements, HtmlTag (que envuelve en una lista de definición) y Form; el código equivalente para crearlos es el siguiente:

$form->setDecorators(array(
    'FormElements',
    array('HtmlTag', array('tag' => 'dl')),
    'Form'
));

Esto crea una salida como la siguiente:

<form action="/form/action" method="post">
<dl>
...
</dl>
</form>

Cualquier atributo establecido en el objeto de formulario se usará como atributos HTML de la etiqueta <form>.

[Note] No es necesario cargar los decoradores por defecto

Por defecto, los decoradores por defecto se cargan durante la inicialización del objeto. Puede deshabilitar esto pasando la opción 'disableLoadDefaultDecorators' al constructor:

$form = new Zend_Form(array('disableLoadDefaultDecorators' => true));

Esta opción puede combinarse con cualquier otra opción que se pase, tanto como opciones de array como en un objeto Zend_Config.

[Note] Uso de múltiples decoradores del mismo tipo

Internamente, Zend_Form usa la clase de un decorador como mecanismo de búsqueda al recuperar decoradores. Como resultado, no puede registrar varios decoradores del mismo tipo; los decoradores posteriores simplemente sobrescribirán los que existían antes.

Para evitar esto, puede usar alias. En lugar de pasar un decorador o un nombre de decorador como primer argumento a addDecorator(), pase un array con un único elemento, con el alias apuntando al objeto o nombre de decorador:

// Alias to 'FooBar':
$form->addDecorator(array('FooBar' => 'HtmlTag'), array('tag' => 'div'));

// And retrieve later:
$form = $element->getDecorator('FooBar');

En los métodos addDecorators() y setDecorators(), deberá pasar la opción 'decorator' en el array que representa al decorador:

// Add two 'HtmlTag' decorators, aliasing one to 'FooBar':
$form->addDecorators(
    array('HtmlTag', array('tag' => 'div')),
    array(
        'decorator' => array('FooBar' => 'HtmlTag'),
        'options' => array('tag' => 'dd')
    ),
);

// And retrieve later:
$htmlTag = $form->getDecorator('HtmlTag');
$fooBar  = $form->getDecorator('FooBar');

Puede crear sus propios decoradores para generar el formulario. Un caso de uso común es cuando conoce exactamente el HTML que desea usar; su decorador podría crear ese HTML exacto y simplemente devolverlo, potencialmente usando los decoradores de elementos individuales o de grupos de visualización.

Los siguientes métodos pueden usarse para interactuar con decoradores:

  • addDecorator($decorator, $options = null)

  • addDecorators(array $decorators)

  • setDecorators(array $decorators)

  • getDecorator($name)

  • getDecorators()

  • removeDecorator($name)

  • clearDecorators()

Zend_Form también usa sobrecarga para permitir renderizar decoradores específicos. __call() interceptará métodos cuyo nombre comience con el texto 'render' y usará el resto del nombre del método para buscar un decorador; si lo encuentra, renderizará ese único decorador. Cualquier argumento pasado al método de llamada se usará como contenido para pasar al método render() del decorador. Como ejemplo:

// Render only the FormElements decorator:
echo $form->renderFormElements();

// Render only the fieldset decorator, passing in content:
echo $form->renderFieldset("<p>This is fieldset content</p>");

Si el decorador no existe, se lanza una excepción.

36.4.7. Validación

Un caso de uso principal de los formularios es validar los datos enviados. Zend_Form permite validar un formulario completo, un formulario parcial, o respuestas para XmlHttpRequests (AJAX). Si los datos enviados no son válidos, dispone de métodos para recuperar los diversos códigos y mensajes de error para elementos y subformularios.

Para validar un formulario completo, use el método isValid():

if (!$form->isValid($_POST)) {
    // failed validation
}

isValid() validará todos los elementos requeridos, y cualquier elemento no requerido contenido en los datos enviados.

En ocasiones necesitará validar solo un subconjunto de los datos; para esto, use isValidPartial($data):

if (!$form->isValidPartial($data)) {
    // failed validation
}

isValidPartial() solo intenta validar los elementos de los datos para los que existen elementos coincidentes; si un elemento no está representado en los datos, se omite.

Al validar elementos o grupos de elementos para una petición AJAX, normalmente estará validando un subconjunto del formulario, y querrá que la respuesta vuelva en JSON. processAjax() hace exactamente eso:

$json = $form->processAjax($data);

Puede entonces simplemente enviar la respuesta JSON al cliente. Si el formulario es válido, esto será una respuesta booleana TRUE. Si no, será un objeto javascript que contiene pares clave/mensaje, donde cada 'message' es un array de mensajes de error de validación.

Para los formularios que fallan la validación, puede recuperar tanto los códigos como los mensajes de error, usando getErrors() y getMessages(), respectivamente:

$codes = $form->getErrors();
$messages = $form->getMessages();
[Note] Nota

Dado que los mensajes devueltos por getMessages() son un array de pares código/mensaje de error, getErrors() normalmente no es necesario.

Puede recuperar códigos y mensajes de error para elementos individuales simplemente pasando el nombre del elemento a cada uno:

$codes = $form->getErrors('username');
$messages = $form->getMessages('username');
[Note] Nota

Nota: al validar elementos, Zend_Form envía un segundo argumento al método isValid() de cada elemento: el array de datos que se está validando. Esto puede luego ser usado por validadores individuales para permitirles utilizar otros valores enviados al determinar la validez de los datos. Un ejemplo sería un formulario de registro que requiere tanto una contraseña como una confirmación de contraseña; el elemento contraseña podría usar la confirmación de contraseña como parte de su validación.

36.4.7.1. Mensajes de error personalizados

En ocasiones, puede que desee especificar uno o más mensajes de error específicos para usar en lugar de los mensajes de error generados por los validadores adjuntos a sus elementos. Además, en ocasiones puede que desee marcar el formulario como inválido usted mismo. Esta funcionalidad es posible mediante los siguientes métodos.

  • addErrorMessage($message): añade un mensaje de error para mostrar en los errores de validación del formulario. Puede llamar a esto más de una vez, y los nuevos mensajes se añaden a la pila.

  • addErrorMessages(array $messages): añade varios mensajes de error para mostrar en los errores de validación del formulario.

  • setErrorMessages(array $messages): añade varios mensajes de error para mostrar en los errores de validación del formulario, sobrescribiendo todos los mensajes de error previamente establecidos.

  • getErrorMessages(): recupera la lista de mensajes de error personalizados que se han definido.

  • clearErrorMessages(): elimina todos los mensajes de error personalizados que se han definido.

  • markAsError(): marca el formulario como que ha fallado la validación.

  • addError($message): añade un mensaje a la pila de mensajes de error personalizados y marca el formulario como inválido.

  • addErrors(array $messages): añade varios mensajes a la pila de mensajes de error personalizados y marca el formulario como inválido.

  • setErrors(array $messages): sobrescribe la pila de mensajes de error personalizados con los mensajes proporcionados y marca el formulario como inválido.

Todos los errores establecidos de esta manera pueden traducirse.

36.4.7.2. Recuperación de solo los valores válidos

Hay escenarios en los que se desea permitir al usuario trabajar en un formulario válido en varios pasos. Mientras tanto, se permite al usuario guardar el formulario con cualquier conjunto de valores intermedios. Luego, si se especifican todos los datos, se puede transferir el modelo de la etapa de construcción o prototipado a una etapa válida.

Puede recuperar todos los valores válidos que coincidan con los datos enviados llamando a:

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

36.4.8. Métodos

A continuación se muestra una lista completa de los métodos disponibles para Zend_Form, agrupados por tipo:

  • Configuración y opciones:

    • setOptions(array $options)

    • setConfig(Zend_Config $config)

  • Cargadores de plugins y rutas:

    • setPluginLoader(Zend_Loader_PluginLoader_Interface $loader, $type = null)

    • getPluginLoader($type = null)

    • addPrefixPath($prefix, $path, $type = null)

    • addPrefixPaths(array $spec)

    • addElementPrefixPath($prefix, $path, $type = null)

    • addElementPrefixPaths(array $spec)

    • addDisplayGroupPrefixPath($prefix, $path)

  • Metadatos:

    • setAttrib($key, $value)

    • addAttribs(array $attribs)

    • setAttribs(array $attribs)

    • getAttrib($key)

    • getAttribs()

    • removeAttrib($key)

    • clearAttribs()

    • setAction($action)

    • getAction()

    • setMethod($method)

    • getMethod()

    • setName($name)

    • getName()

  • Elementos:

    • addElement($element, $name = null, $options = null)

    • addElements(array $elements)

    • setElements(array $elements)

    • getElement($name)

    • getElements()

    • removeElement($name)

    • clearElements()

    • setDefaults(array $defaults)

    • setDefault($name, $value)

    • getValue($name)

    • getValues()

    • getUnfilteredValue($name)

    • getUnfilteredValues()

    • setElementFilters(array $filters)

    • setElementDecorators(array $decorators)

  • Subformularios:

    • addSubForm(Zend_Form $form, $name, $order = null)

    • addSubForms(array $subForms)

    • setSubForms(array $subForms)

    • getSubForm($name)

    • getSubForms()

    • removeSubForm($name)

    • clearSubForms()

    • setSubFormDecorators(array $decorators)

  • Grupos de visualización:

    • addDisplayGroup(array $elements, $name, $options = null)

    • addDisplayGroups(array $groups)

    • setDisplayGroups(array $groups)

    • getDisplayGroup($name)

    • getDisplayGroups()

    • removeDisplayGroup($name)

    • clearDisplayGroups()

    • setDisplayGroupDecorators(array $decorators)

  • Validación

    • populate(array $values)

    • isValid(array $data)

    • isValidPartial(array $data)

    • processAjax(array $data)

    • persistData()

    • getErrors($name = null)

    • getMessages($name = null)

  • Renderizado:

    • setView(Zend_View_Interface $view = null)

    • getView()

    • addDecorator($decorator, $options = null)

    • addDecorators(array $decorators)

    • setDecorators(array $decorators)

    • getDecorator($name)

    • getDecorators()

    • removeDecorator($name)

    • clearDecorators()

    • render(Zend_View_Interface $view = null)

  • I18n:

    • setTranslator(Zend_Translate_Adapter $translator = null)

    • getTranslator()

    • setDisableTranslator($flag)

    • translatorIsDisabled()

36.4.9. Configuración

Zend_Form es totalmente configurable mediante setOptions() y setConfig() (o pasando opciones o un objeto Zend_Config al constructor). Usando estos métodos, puede especificar elementos de formulario, grupos de visualización, decoradores y metadatos.

Como regla general, si 'set' + la clave de la opción se refiere a un método de Zend_Form, entonces el valor proporcionado se pasará a ese método. Si el método de acceso no existe, se asume que la clave hace referencia a un atributo, y se pasará a setAttrib().

Las excepciones a la regla incluyen las siguientes:

  • prefixPath se pasará a addPrefixPaths()

  • elementPrefixPath se pasará a addElementPrefixPaths()

  • displayGroupPrefixPath se pasará a addDisplayGroupPrefixPaths()

  • los siguientes setters no pueden establecerse de esta manera:

    • setAttrib (aunque setAttribs *sí* funciona)

    • setConfig

    • setDefault

    • setOptions

    • setPluginLoader

    • setSubForms

    • setTranslator

    • setView

Como ejemplo, aquí hay un archivo de configuración que pasa configuración para cada tipo de dato configurable:

[element]
name = "registration"
action = "/user/register"
method = "post"
attribs.class = "zend_form"
attribs.onclick = "validate(this)"

disableTranslator = 0

prefixPath.element.prefix = "My_Element"
prefixPath.element.path = "My/Element/"
elementPrefixPath.validate.prefix = "My_Validate"
elementPrefixPath.validate.path = "My/Validate/"
displayGroupPrefixPath.prefix = "My_Group"
displayGroupPrefixPath.path = "My/Group/"

elements.username.type = "text"
elements.username.options.label = "Username"
elements.username.options.validators.alpha.validator = "Alpha"
elements.username.options.filters.lcase = "StringToLower"
; more elements, of course...

elementFilters.trim = "StringTrim"
;elementDecorators.trim = "StringTrim"

displayGroups.login.elements.username = "username"
displayGroups.login.elements.password = "password"
displayGroupDecorators.elements.decorator = "FormElements"
displayGroupDecorators.fieldset.decorator = "Fieldset"

decorators.elements.decorator = "FormElements"
decorators.fieldset.decorator = "FieldSet"
decorators.fieldset.decorator.options.class = "zend_form"
decorators.form.decorator = "Form"

Lo anterior podría abstraerse fácilmente a un archivo de configuración basado en XML o en un array de PHP.

36.4.10. Formularios personalizados

Una alternativa al uso de formularios basados en configuración es subclasificar Zend_Form. Esto tiene varios beneficios:

  • Puede probar unitariamente su formulario fácilmente para asegurar que las validaciones y el renderizado se comportan como se espera.

  • Control detallado sobre elementos individuales.

  • Reutilización de objetos de formulario, y mayor portabilidad (sin necesidad de rastrear archivos de configuración).

  • Implementación de funcionalidad personalizada.

El caso de uso más típico sería usar el método init() para configurar elementos de formulario específicos y la configuración:

class My_Form_Login extends Zend_Form
{
    public function init()
    {
        $username = new Zend_Form_Element_Text('username');
        $username->class = 'formtext';
        $username->setLabel('Username:')
                 ->setDecorators(array(
                     array('ViewHelper',
                           array('helper' => 'formText')),
                     array('Label',
                           array('class' => 'label'))
                 ));

        $password = new Zend_Form_Element_Password('password');
        $password->class = 'formtext';
        $password->setLabel('Password:')
                 ->setDecorators(array(
                     array('ViewHelper',
                           array('helper' => 'formPassword')),
                     array('Label',
                           array('class' => 'label'))
                 ));

        $submit = new Zend_Form_Element_Submit('login');
        $submit->class = 'formsubmit';
        $submit->setValue('Login')
               ->setDecorators(array(
                   array('ViewHelper',
                   array('helper' => 'formSubmit'))
               ));

        $this->addElements(array(
            $username,
            $password,
            $submit
        ));

        $this->setDecorators(array(
            'FormElements',
            'Fieldset',
            'Form'
        ));
    }
}

Este formulario puede entonces instanciarse simplemente con:

$form = new My_Form_Login();

y toda la funcionalidad ya está configurada y lista; no se necesitan archivos de configuración. (Tenga en cuenta que este ejemplo está muy simplificado, ya que no contiene validadores ni filtros para los elementos.)

Otra razón común para la extensión es definir un conjunto de decoradores por defecto. Puede hacer esto sobrescribiendo el método loadDefaultDecorators():

class My_Form_Login extends Zend_Form
{
    public function loadDefaultDecorators()
    {
        $this->setDecorators(array(
            'FormElements',
            'Fieldset',
            'Form'
        ));
    }
}