Rendre visuellement un objet de formulaire est complètement optionnel -- il n'est pas
obligatoire d'utiliser la méthode render()
de Zend_Form
.
Cependant, si vous l'utilisez, alors des décorateurs sont utilisés pour rendre les
différents objets du formulaire.
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.
La configuration du décorateur est effectuée via son constructeur ou sa méthode
setOptions()
. Lorsque vous créez des décorateurs au travers de méthodes comme
addDecorator()
, 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.
Avant le rendu d'un décorateur, au travers de sa méthode render()
,
l'objet sur lequel il agit lui est passé en argument, grâce à setElement()
, 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é.
Pour configurer un décorateur, passez un tableau d'options ou un objet
Zend_Config
à son constructeur. Aussi, un tableau peut être passé
à setOptions()
, ou un objet Zend_Config
à
setConfig()
.
Options de base:
-
placement
: le placement peut être 'append' ou 'prepend' (insensible à la casse), et indique sur le contenu passé àrender()
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'. -
separator
: le séparateur est utilisé entre le contenu passé àrender()
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 vautPHP_EOL
.
L'interface des décorateurs spécifie les méthodes pour agir sur les options. Les voici :
-
setOption($key, $value)
: affecte une option. -
getOption($key)
: récupère une option. -
getOptions()
: récupère toutes les options. -
removeOption($key)
: supprime une option. -
clearOptions()
: supprime toutes les options.
Les décorateurs sont destinés à agir avec tous les objets du formulaire,
Zend_Form
, Zend_Form_Element
,
Zend_Form_DisplayGroup
, et toute classe en dérivant. La méthode
setElement()
vous permet de passer l'objet au décorateur sur lequel il
travaille. getElement()
vous permet de récupérer cet objet depuis le
décorateur.
Chaque méthode render()
des décorateurs accepte en paramètre une
chaîne $content
. 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é.
Zend_Form
est fourni avec quelques décorateurs de base,
voyez le chapitre sur les décorateurs
standards pour plus de détails.
Si vos rendus HTML sont complexes, ou si vous avez besoin de beaucoup de personnalisation, vous pouvez alors créer vos propres décorateurs.
Les décorateurs ont juste besoin d'implémenter l'interface
Zend_Form_Decorator_Interface
. Celle-ci spécifie les méthodes
suivantes :
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); }
Pour vous simplifier la tâche, vous pourriez considérer le fait d'étendre
Zend_Form_Decorator_Abstract
, qui implémente toutes les méthodes de
l'interface sauf render()
.
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 div
. Voici comment vous pourriez
procéder :
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; } } }
Vous pouvez maintenant placer ce décorateur dans les chemins des décorateurs :
// pour un élément: $element->addPrefixPath('My_Decorator', 'My/Decorator/', 'decorator'); // pour tous les éléments: $form->addElementPrefixPath('My_Decorator', 'My/Decorator/', 'decorator');
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 :
// Ecrase les éventuels décorateurs de cet élément avec le notre: $element->setDecorators(array('Composite'));
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.
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' :
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; } } }
Dans cet exemple particulier, comme le segment final du nom de la classe,
'Errors', respecte la syntaxe de Zend_Form_Decorator_Errors
, il
sera alors utilisé à la place du 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.
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).
Pour effectuer ceci, appelez simplement render[nom-du-décorateur]()
,
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 :
// 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');
Si le décorateur n'existe pas, une exception sera levée.
Ceci peut être particulièrement utile lors du rendu d'un formulaire avec le décorateur ViewScript ; là où chaque élément utilise ses décorateurs pour rendre du contenu, mais de manière très fine.