<sect1 id="zend.form.decorators">
    <title>Creando un personalizado marcado de formulario usando Zend_Form_Decorator</title>

    <para>
    	Representando un objeto form es completamente opcional -- usted no necesita usar
    	los métodos <code>Zend_Form</code> render() en absoluto. Sin embargo, si lo hace,
        los decoradores son usados para representar varios objetos form.
    </para>

    <para>
    	Un arbitrario número 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, se puede reemplazar el contenido que se ha pasado o adjunto o 
    	pre enviado. 
    </para>

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

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

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

        <para>
            Para configurar un decorador, pasar una matriz de opciones o un
            objeto <code>Zend_Config</code> a este constructor, a una matriz
            <code>setOptions()</code>, o a un objeto <code>Zend_Config</code>
            <code>setConfig()</code>.
        </para>

        <para>
            Opciones standarizadas incluyen:
        </para>

        <itemizedlist>
            <listitem><para>
                    <code>placement</code>: La ubicación puede ser cualquiera de los dos 'append' o
                    'prepend' (caso insensitivo) e indica cualquier contenido
                    pasado a <code>render()</code> será adjuntado 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>
            		<code>separator</code>: El separator es usado entre el 
            		contenido pasado a <code>render()</code> 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 <code>PHP_EOL</code>.
            </para></listitem>
        </itemizedlist>

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

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

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

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

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

            <listitem><para>
                    <code>clearOptions()</code>: remover o eliminar todas las opciones.
            </para></listitem>
        </itemizedlist>

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

        <para>
            Cada método decorador <code>render()</code> acepta una cadena
            <code>$content</code>. 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>
        	<code>Zend_Form</code> 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 usted encuentra que sus necesidades son complejas o necesita una enorme
        	personalización, usted debería considerar crear un personalizado decorador.
        </para>

        <para>
        	Decoradores necesitan solo implementar
            <code>Zend_Decorator_Interface</code>. La interface especifica lo
            siguiente:
        </para>

        <programlisting role="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, usted puede simplemente extender
        	<code>Zend_Decorator_Abstract</code>, el cual implementa todos los métodos
            excepto <code>render()</code>.
        </para>

        <para>
        	Como ejemplo, vamos a decir quue usted quiere reducir el número de
        	decoradores que utiliza, y construir un "compuesto" decorador que cuidó
        	la etiqueta generadora, elemento, algún mensaje de error, y describir 
        	en HTML <code>div</code>. Usted puede construir como un 'Compuesto'
            decorador como sigue:
        </para>

        <programlisting role="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>
        	Usted puede entonces ubicarlo en el directorio decorador:
        </para>

        <programlisting role="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>
        	Usted puede especificar este decorador como 'Compuesto' y adjuntarlo a 
        	un elemento:
        </para>

        <programlisting role="php"><![CDATA[
// Overwrite existing decorators with this single one:
$element->setDecorators(array('Composite'));
]]>
        </programlisting>

        <para>
        	Mientras este ejemplo mostró cómo crear un decorador que genera
        	salidas complejas de muchas propiedades de elementos, usted puede también crear
        	decoradores que manejen un solo aspecto de un elemento; los decoradores
        	'Decorador' y 'etiqueta' 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 usted quiere simplemente desplegar que un error a ocurrido
        	cuando validabamos un elemento, pero no desplegar individualmente 
        	cada uno de los mensajes de error, usted podría crear su propio 
        	decorador 'Errores':
        </para>

        <programlisting role="php"><![CDATA[
class My_Decorator_Errors
{
    public function render($content = '')
    {
        $output = '<div class="errors">The value you provided was invalid;
            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 <code>Zend_Form_Decorator_Errors</code>,
            será generado <emphasis>en lugar de</emphasis> el decorador
            -- significa que usted no necesitará cambiar ningún decorador para modificar la
            salida. Nombrando sus decoradores después de los existentes decoradores 
            estándar, usted puede modificar decoradores sin necesitad de modificar sus
            elementos decoradores.
        </para>
    </sect2>

    <sect2 id="zend.form.decorators.individual">
        <title>Generando individuales decoradores</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 <code>render[DecoratorName]()</code>, cuando
        	"[DecoratorName]" es el "nombre corto" de su decorador; opcionalmente,
        	usted puede pasar en el contenido lo que usted quiera. Por ejemplo:
        </para>

        <programlisting role="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 concreto control.
        </para>
    </sect2>
</sect1>
<!--
vim:se ts=4 sw=4 et:
-->
