<?xml version="1.0" encoding="UTF-8"?>
<!-- EN-Revision: 15103 -->
<!-- Reviewed: no -->
<sect1 id="zend.form.decorators">
    <title>Creando un personalizado marcado de formulario usando Zend_Form_Decorator</title>

    <para>
        Representar un objeto form es completamente opcional -- no está obligado a usar
        los métodos <classname>Zend_Form</classname> render() en absoluto. Sin embargo, si lo hace,
        los decoradores se usan para representar distintos objetos form.
    </para>

    <para>
        Un número arbitrario de decoradores pueden estar junto a cada elemento
        (elements, display groups, sub forms o el objeto form por si mismo);
        Sin embargo, solo un decorador de un tipo dado puede estar al lado de cada
        elemento. Los decoradores son llamados en el orden en que han sido introducidos. Dependiendo
        del decorador, puede reemplazar el contenido que se ha pasado, postponerlo o
        anteponerlo.
    </para>

    <para>
        El estado del objeto es determinado a través de las opciones de configuración pasadas al constructor
        o el método decorador <methodname>setOptions()</methodname>. Cuando se crean
        decoradores mediante funciones <methodname>addDecorator()</methodname> o métodos relacionados,
        las opciones pueden ser pasadas como argumentos al método. Esto puese ser usado para
        una ubicación especifica, un separador se usa entre el contenido pasado y el
        nuevo contenido generado y cualquier opción que el decorador soporte.
    </para>

    <para>
        Antes de que el <methodname>render()</methodname> de cada decorador sea llamado, el
        item actual es determinado en el decorador usando <methodname>setElement()</methodname>,
        dando al decorador conocimiento del item representado. Esto permite
        crear decoradores que sólo representan porciones especificas del item
        -- tal como etiquetas, el valor, mensajes de error, etc. Encadenando
        muchos decoradores que representan especificos segmentos, puede construir
        marcados complejos representando al item entero.
    </para>

    <sect2 id="zend.form.decorators.operation">
        <title>Operación</title>

        <para>
            Para configurar un decorador, pase un array de opciones o un
            objeto <classname>Zend_Config</classname> a este constructor, a un array
            <methodname>setOptions()</methodname>, o a un objeto <classname>Zend_Config</classname>
            <methodname>setConfig()</methodname>.
        </para>

        <para>
            Opciones estándar incluyen:
        </para>

        <itemizedlist>
            <listitem><para>
                    <methodname>placement</methodname>: La ubicación puede ser cualquiera de los dos 'append' o
                    'prepend' (caso insensitivo) e indica cualquier contenido
                    pasado a <methodname>render()</methodname> será postpuesto o
                    antepuesto respectivamente. En el caso de que el decorador
                    reemplace el contenido, esta configuración es ignorada. La configuración
                    por defecto es adjuntada.
            </para></listitem>

            <listitem><para>
                    <methodname>separator</methodname>: El separator es usado entre el
                    contenido pasado a <methodname>render()</methodname> y el nuevo contenido
                    generado por el decorador, o entre items generados por el
                    decorador (ejemplo FormElements usa el separador entre cada
                    item generado). En el caso que un decorador reemplace el
                    contenido, esta configuración puede ser ignorada. El valor por defecto
                    es <methodname>PHP_EOL</methodname>.
            </para></listitem>
        </itemizedlist>

        <para>
            La interface del decorador especifica los métodos para interactuar con las
            opciones. Esto incluye:
        </para>

        <itemizedlist>
            <listitem><para>
                    <methodname>setOption($key, $value)</methodname>: determina una sola opción.
            </para></listitem>

            <listitem><para>
                    <methodname>getOption($key)</methodname>: recuperar un solo valor de opción.
            </para></listitem>

            <listitem><para>
                    <methodname>getOptions()</methodname>: recuperar todas las opciones.
            </para></listitem>

            <listitem><para>
                    <methodname>removeOption($key)</methodname>: eliminar una sola opción.
            </para></listitem>

            <listitem><para>
                    <methodname>clearOptions()</methodname>: eliminar todas las opciones.
            </para></listitem>
        </itemizedlist>

        <para>
            Decoradores son diseñados para interactuar con varios
            tipos de clases <classname>Zend_Form</classname>: <classname>Zend_Form</classname>,
            <classname>Zend_Form_Element</classname>, <methodname>Zend_Form_DisplayGroup</methodname>,
            y todas las clases derivan de ellas. El método
            <methodname>setElement()</methodname> permite determinar el objeto del
            decorador que esta actualmente trabajando con, y <methodname>getElement()</methodname>
            es usado para recuperarlo.
        </para>

        <para>
            Cada método decorador <methodname>render()</methodname> acepta una cadena
            <methodname>$content</methodname>. Cuando el primer decorador es llamado, esta
            cadena esta tipicamente vacía, mientras las subsecuentes llamadas serán
            puestas. Basados en el tipo de decorador y en las opciones pasadas,
            el decorador ya sea reemplazará la cadena, antenpodrá la cadena
            o adjuntará la cadena; una separador opcional será usado en las
            dos últimas situaciones.
        </para>
    </sect2>

    <sect2 id="zend.form.decorators.standard">
        <title>Decoradores estándar</title>

        <para>
            <classname>Zend_Form</classname> entrega muchos decoradores estándar; ver
            <link linkend="zend.form.standardDecorators">el capítulo Decoradores
            estándar</link> para detalles.
        </para>
    </sect2>

    <sect2 id="zend.form.decorators.custom">
        <title>Decoradores personalizados</title>

        <para>
            Si encuentra que sus necesidades son complejas o necesita una enorme
            personalización, debería considerar crear un decorador personalizado.
        </para>

        <para>
            Los decoradores necesitan implementar sólo
           <classname>Zend_Form_Decorator_Interface</classname>. La interface especifica lo
            siguiente:
        </para>

         <programlisting language="php"><![CDATA[
interface Zend_Decorator_Interface
{
    public function __construct($options = null);
    public function setElement($element);
    public function getElement();
    public function setOptions(array $options);
    public function setConfig(Zend_Config $config);
    public function setOption($key, $value);
    public function getOption($key);
    public function getOptions();
    public function removeOption($key);
    public function clearOptions();
    public function render($content);
}
]]></programlisting>

        <para>
            Para hacerlo mas simple, simplemente puede extender
            <classname>Zend_Form_Decorator_Abstract</classname>, el cual implementa todos los métodos
            excepto <methodname>render()</methodname>.
        </para>

        <para>
            Como ejemplo, digamos que quiere reducir el número de
            decoradores que utiliza, y construir un decorador compuesto que se
            encargó de renderizar la etiqueta generadora, el elemento, cualquier
            mensaje de error, y descripción en un <methodname>div</methodname> HTML.
            Puede construir como un decorador compuesto como sigue:
        </para>

         <programlisting language="php"><![CDATA[
class My_Decorator_Composite extends Zend_Form_Decorator_Abstract
{
    public function buildLabel()
    {
        $element = $this->getElement();
        $label = $element->getLabel();
        if ($translator = $element->getTranslator()) {
            $label = $translator->translate($label);
        }
        if ($element->isRequired()) {
            $label .= '*';
        }
        $label .= ':';
        return $element->getView()
                       ->formLabel($element->getName(), $label);
    }

    public function buildInput()
    {
        $element = $this->getElement();
        $helper  = $element->helper;
        return $element->getView()->$helper(
            $element->getName(),
            $element->getValue(),
            $element->getAttribs(),
            $element->options
        );
    }

    public function buildErrors()
    {
        $element  = $this->getElement();
        $messages = $element->getMessages();
        if (empty($messages)) {
            return '';
        }
        return '<div class="errors">' .
               $element->getView()->formErrors($messages) . '</div>';
    }

    public function buildDescription()
    {
        $element = $this->getElement();
        $desc    = $element->getDescription();
        if (empty($desc)) {
            return '';
        }
        return '<div class="description">' . $desc . '</div>';
    }

    public function render($content)
    {
        $element = $this->getElement();
        if (!$element instanceof Zend_Form_Element) {
            return $content;
        }
        if (null === $element->getView()) {
            return $content;
        }

        $separator = $this->getSeparator();
        $placement = $this->getPlacement();
        $label     = $this->buildLabel();
        $input     = $this->buildInput();
        $errors    = $this->buildErrors();
        $desc      = $this->buildDescription();

        $output = '<div class="form element">'
                . $label
                . $input
                . $errors
                . $desc
                . '</div>'

        switch ($placement) {
            case (self::PREPEND):
                return $output . $separator . $content;
            case (self::APPEND):
            default:
                return $content . $separator . $output;
        }
    }
}
]]></programlisting>

        <para>
            Puede entonces ubicarlo en el directorio del decorador:
        </para>

         <programlisting language="php"><![CDATA[
// para un elemento:
$element->addPrefixPath('My_Decorator',
                        'My/Decorator/',
                        'decorator');

// para todos los elementos:
$form->addElementPrefixPath('My_Decorator',
                            'My/Decorator/',
                            'decorator');
]]></programlisting>

        <para>
            Puede especificar este decorador como compuesto (composite) y adjuntarlo a
            un elemento:
        </para>

         <programlisting language="php"><![CDATA[
// Sobreescribe los decoradores existentes con este otro:
$element->setDecorators(array('Composite'));
]]></programlisting>

        <para>
            Mientras este ejemplo mostró cómo crear un decorador que genera
            salidas complejas de muchas propiedades de elementos, puede también crear
            decoradores que manejen un solo aspecto de un elemento; los decoradores
            'Decorator' y 'Label' son excelentes ejemplos para
            esta práctica. Hacerlo le permite mezclar y combinar decoradores para llegar
            a complejas salidas -- y también anular aspectos de decoración para
            personalizar sus necesidades.
        </para>

        <para>
            Por ejemplo, si quiere simplemente desplegar que un error ha ocurrido
            cuando validábamos un elemento, pero no desplegar individualmente
            cada uno de los mensajes de error, usted podría crear su propio
            decorador 'Errores':
        </para>

         <programlisting language="php"><![CDATA[
class My_Decorator_Errors
{
    public function render($content = '')
    {
        $output = '<div class="errors">El valor que proporcionó no es válido;
            please try again</div>';

        $placement = $this->getPlacement();
        $separator = $this->getSeparator();

        switch ($placement) {
            case 'PREPEND':
                return $output . $separator . $content;
            case 'APPEND':
            default:
                return $content . $separator . $output;
        }
    }
}
]]></programlisting>

        <para>
            En este ejemplo particular, debido al segmento del decorador final,
            'Errors', se combina como <classname>Zend_Form_Decorator_Errors</classname>,
            será generado <emphasis>en lugar de</emphasis> el decorador
            -- significa que no necesitará cambiar ningún decorador para modificar la
            salida. Nombrando sus decoradores después de los decoradores existentes
            estándar, usted puede modificar decoradores sin necesitad de modificar sus
            elementos decoradores.
        </para>
    </sect2>

    <sect2 id="zend.form.decorators.individual">
        <title>Generando decoradores individuales</title>

        <para>
            Desde que los decoradores pueden capturar distintos metadatos del elemento o formulario
            que ellos decoran, es a menudo útil generar un decorador individual.
            Afortunadamente, esta caracteristica es posible inicializando el método
            en cada tipo de clase form (forms, sub form, display group,
            element).
        </para>

        <para>
            Para hacer eso, simplemente <methodname>render[DecoratorName]()</methodname>, cuando
            "[DecoratorName]" es el "nombre corto" de su decorador; opcionalmente,
            puede pasar en el contenido lo que usted quiera. Por ejemplo:
        </para>

         <programlisting language="php"><![CDATA[
// genera el elemento decorador label:
echo $element->renderLabel();

// genera sólo el campo display group, con algún contenido:
echo $group->renderFieldset('fieldset content');

// genera sólo el formulario HTML, con algún contenido:
echo $form->renderHtmlTag('wrap this content');
]]></programlisting>

        <para>
            Si el decorador no existe, una excepción es inicializada.
        </para>

        <para>
            Esto puede ser útil particularmente cuando se genera un formulario con el
            decorador ViewScript; cada elemento puede usar sus decoradores adjuntos
            para generar contenido, pero con un control minucioso.
        </para>
    </sect2>
</sect1>
<!--
vim:se ts=4 sw=4 et:
-->
