<?xml version="1.0" encoding="UTF-8"?>
<!-- EN-Revision: 15643 -->
<!-- Reviewed: no -->
<sect1 id="zend.form.decorators">
    <title>Créer un visuel personnalisé en utilisant Zend_Form_Decorator</title>

    <para>
        Rendre visuellement un objet de formulaire est complètement optionnel -- il n'est pas
        obligatoire d'utiliser la méthode <code>render()</code> de <classname>Zend_Form</classname>.
        Cependant, si vous l'utilisez, alors des décorateurs sont utilisés pour rendre les
        différents objets du formulaire.
    </para>

    <para>
        Un nombre arbitraire de décorateurs peut être attaché à chaque objet du formulaire
        (élément, groupe d'affichage, sous-formulaires ou encore l'objet formulaire lui-même) ;
        cependant seul un décorateur par type peut être attaché. Les décorateurs sont appelés dans
        l'ordre dans lequel ils ont été enregistrés. En fonction du décorateur en question, celui-ci
        peut remplacer le contenu qui lui est passé, ou alors le faire précédé ou suivre.
    </para>

    <para>
        La configuration du décorateur est effectuée via son constructeur ou sa méthode
        <code>setOptions()</code>. Lorsque vous créez des décorateurs au travers de méthodes comme
        <code>addDecorator()</code>, alors sa configuration doit être passée en tant que tableau à
        ladite méthode. Ces options de configuration peuvent être utilisées pour indiquer le
        placement du décorateur, le séparateur inter-éléments ou toute autre option
        acceptable.
    </para>

    <para>
        Avant le rendu d'un décorateur, au travers de sa méthode <code>render()</code>,
        l'objet sur lequel il agit lui est passé en argument, grâce à <code>setElement()</code>, et
        ainsi le décorateur peut piloter l'élément sur lequel il agit. Ceci permet de créer des
        décorateurs qui n'agissent que sur un petit paramètre de l'élément auquel ils sont
        rattachés, comme le label, les messages d'erreur, etc... En chaînant des décorateurs qui
        rendent chacun individuellement un petit morceau d'un élément, vous pouvez créer une mise en
        forme complexe représentant l'objet (élément) dans son intégralité.
    </para>

    <sect2 id="zend.form.decorators.operation">
        <title>Configuration</title>

        <para>
            Pour configurer un décorateur, passez un tableau d'options ou un objet
            <classname>Zend_Config</classname> à son constructeur. Aussi, un tableau peut être passé
            à <code>setOptions()</code>, ou un objet <classname>Zend_Config</classname> à
            <code>setConfig()</code>.
        </para>

        <para>Options de base:</para>

        <itemizedlist>
            <listitem>
                <para>
                    <code>placement</code>&#160;: le placement peut être 'append' ou 'prepend'
                    (insensible à la casse), et indique sur le contenu passé à <code>render()</code>
                    doit être ajouté après ou avant, respectivement. Dans le cas où le décorateur
                    remplace le contenu, cette directive de placement est ignorée. La directive par
                    défaut est 'append'.
                </para>
            </listitem>

            <listitem>
                <para>
                    <code>separator</code>&#160;: le séparateur est utilisé entre le contenu passé
                    à <code>render()</code> et le nouveau contenu généré par le décorateur, ou
                    encore entre les éléments rendus (par exemple FormElements utilise le séparateur
                    entre chaque objet rendu). Dans le cas où le décorateur remplace son contenu,
                    cette option est ignorée. Par défaut, elle vaut <code>PHP_EOL</code>.
                </para>
            </listitem>
        </itemizedlist>

        <para>
            L'interface des décorateurs spécifie les méthodes pour agir sur les options. Les
            voici&#160;:
        </para>

        <itemizedlist>
            <listitem>
                <para><code>setOption($key, $value)</code>&#160;: affecte une option.</para>
            </listitem>

            <listitem>
                <para><code>getOption($key)</code>&#160;: récupère une option.</para>
            </listitem>

            <listitem>
                <para><code>getOptions()</code>&#160;: récupère toutes les options.</para>
            </listitem>

            <listitem>
                <para><code>removeOption($key)</code>&#160;: supprime une option.</para>
            </listitem>

            <listitem>
                <para><code>clearOptions()</code>&#160;: supprime toutes les options.</para>
            </listitem>
        </itemizedlist>

        <para>
            Les décorateurs sont destinés à agir avec tous les objets du formulaire,
            <classname>Zend_Form</classname>, <classname>Zend_Form_Element</classname>,
            <classname>Zend_Form_DisplayGroup</classname>, et toute classe en dérivant. La méthode
            <code>setElement()</code> vous permet de passer l'objet au décorateur sur lequel il
            travaille. <code>getElement()</code> vous permet de récupérer cet objet depuis le
            décorateur.
        </para>

        <para>
            Chaque méthode <code>render()</code> des décorateurs accepte en paramètre une
            chaîne <code>$content</code>. Lorsque le premier décorateur est appelé, cette chaîne est
            en toute logique vide, alors que tous les appels successifs travailleront sur le contenu
            précédent. Selon le type de décorateur et ses options, la chaîne sera alors remplacée,
            précédée ou suivie du nouveau contenu décoré. Dans ces deux derniers cas, un séparateur
            optionnel peut être utilisé.
        </para>
    </sect2>

    <sect2 id="zend.form.decorators.standard">
        <title>Décorateurs standards</title>

        <para>
            <classname>Zend_Form</classname> est fourni avec quelques décorateurs de base,
            voyez <link linkend="zend.form.standardDecorators">le chapitre sur les décorateurs
            standards</link> pour plus de détails.
        </para>
    </sect2>

    <sect2 id="zend.form.decorators.custom">
        <title>Décorateurs personnalisés</title>

        <para>
            Si vos rendus HTML sont complexes, ou si vous avez besoin de beaucoup de
            personnalisation, vous pouvez alors créer vos propres décorateurs.
        </para>

        <para>
            Les décorateurs ont juste besoin d'implémenter l'interface
            <classname>Zend_Form_Decorator_Interface</classname>. Celle-ci spécifie les méthodes
            suivantes&#160;:
        </para>

        <programlisting language="php"><![CDATA[
interface Zend_Form_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>
            Pour vous simplifier la tâche, vous pourriez considérer le fait d'étendre
            <classname>Zend_Form_Decorator_Abstract</classname>, qui implémente toutes les méthodes de
            l'interface sauf <code>render()</code>.
        </para>

        <para>
            Par exemple, imaginons que vous ne souhaitiez pas vous encombrer avec un nombre
            important de décorateurs, et que vous vouliez afficher les principale caractéristiques
            d'un élément grâce à un seul décorateur (label, élément, messages d'erreur et
            description), le tout dans une <code>div</code>. Voici comment vous pourriez
            procéder&#160;:
        </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>
            Vous pouvez maintenant placer ce décorateur dans les chemins des décorateurs&#160;:
        </para>

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

// pour tous les éléments:
$form->addElementPrefixPath('My_Decorator',
                            'My/Decorator/',
                            'decorator');
]]></programlisting>

        <para>
            Dès à présent, vous pouvez indiquer que vous voulez utiliser le décorateur
            'Composite', (c'est son nom de classe sans le préfixe) et l'attacher à un
            élément&#160;:
        </para>

        <programlisting language="php"><![CDATA[
// Ecrase les éventuels décorateurs de cet élément avec le notre:
$element->setDecorators(array('Composite'));
]]></programlisting>

        <para>
            Cet exemple vous montre comment rendre un contenu HTML complexe à partir de
            propriétés d'un élément, en une seule passe. Il existe des décorateurs qui ne s'occupent
            que d'une propriété de l'élément auquel ils sont rattachés, 'Errors' et 'Label' en sont
            d'excellents exemples qui permettent un placement fin.
        </para>

        <para>
            Par exemple, si vous souhaitez simplement informer l'utilisateur d'une erreur,
            mais sans lui montrer les messages d'erreurs, vous pouvez créer votre propre décorateur
            'Errors'&#160;:
        </para>

        <programlisting language="php"><![CDATA[
class My_Decorator_Errors
{
    public function render($content = '')
    {
        $output = '<div class="errors">La valeur est invalide, rééssayez</div>';

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

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

        <para>
            Dans cet exemple particulier, comme le segment final du nom de la classe,
            'Errors', respecte la syntaxe de <classname>Zend_Form_Decorator_Errors</classname>, il
            sera alors utilisé <emphasis>à la place du</emphasis> décorateur par défaut -- ceci
            signifie que vous n'avez pas besoin de l'injecter dans un élément particulier. En
            nommant vos fins de classes de décorateurs comme celles des décorateurs standards, vous
            pouvez changer la décoration sans agir sur les éléments en question.
        </para>
    </sect2>

    <sect2 id="zend.form.decorators.individual">
        <title>Rendre des décorateurs individuellement</title>

        <para>
            Comme les décorateurs agissent souvent sur une propriété d'un élément, et en
            fonction de la décoration précédente, il peut être utile d'afficher juste le rendu d'un
            décorateur particulier, sur un élément. Ceci est possible par la surcharge des méthodes
            dans les classes principales de Zend_Form (formulaires, sous-formulaires, groupes
            d'affichage, éléments).
        </para>

        <para>
            Pour effectuer ceci, appelez simplement <code>render[nom-du-décorateur]()</code>,
            où "[nom-du-décorateur]" est le nom court de votre décorateur (sans son préfixe de
            classe). Il est aussi possible de lui passer optionnellement du contenu, par
            exemple&#160;:
        </para>

        <programlisting language="php"><![CDATA[
// rend juste le décorateur Label de cet élément:
echo $element->renderLabel();

// Rend juste le décorateur Fieldset, en lui passant du contenu:
echo $group->renderFieldset('fieldset content');

// rend juste le tag HTML du formulaire, avec du contenu:
echo $form->renderHtmlTag('wrap this content');
]]></programlisting>

        <para>Si le décorateur n'existe pas, une exception sera levée.</para>

        <para>
            Ceci peut être particulièrement utile lors du rendu d'un formulaire avec le
            décorateur ViewScript&#160;; là où chaque élément utilise ses décorateurs pour rendre du
            contenu, mais de manière très fine.
        </para>
    </sect2>
</sect1>
<!--
vim:se ts=4 sw=4 et:
-->