PhpRiot
Become Zend Certified

Prepare for the ZCE exam using our quizzes (web or iPad/iPhone). More info...


When you're ready get 7.5% off your exam voucher using voucher CJQNOV23 at the Zend Store

Decorators

One particular pain point for many web developers is the creation of the XHTML forms themselves. For each element, the developer needs to create markup for the element itself (typically a label) and special markup for displaying validation error messages. The more elements on the page, the less trivial this task becomes.

Zend_Form_Element tries to solve this issue through the use of "decorators". Decorators are simply classes that have access to the element and a method for rendering content. For more information on how decorators work, please see the section on Zend_Form_Decorator.

The default decorators used by Zend_Form_Element are:

  • ViewHelper: specifies a view helper to use to render the element. The 'helper' element attribute can be used to specify which view helper to use. By default, Zend_Form_Element specifies the 'formText' view helper, but individual subclasses specify different helpers.

  • Errors: appends error messages to the element using Zend_View_Helper_FormErrors. If none are present, nothing is appended.

  • Description: appends the element description. If none is present, nothing is appended. By default, the description is rendered in a <p> tag with a class of 'description'.

  • HtmlTag: wraps the element and errors in an HTML <dd> tag.

  • Label: prepends a label to the element using Zend_View_Helper_FormLabel, and wraps it in a <dt> tag. If no label is provided, just the definition term tag is rendered.

Default Decorators Do Not Need to Be Loaded

By default, the default decorators are loaded during object initialization. You can disable this by passing the 'disableLoadDefaultDecorators' option to the constructor:

<?php
$element 
= new Zend_Form_Element('foo',
                                 array(
'disableLoadDefaultDecorators' =>
                                      
true)
                                );

This option may be mixed with any other options you pass, both as array options or in a Zend_Config object.

Since the order in which decorators are registered matters- the first decorator registered is executed first- you will need to make sure you register your decorators in an appropriate order, or ensure that you set the placement options in a sane fashion. To give an example, here is the code that registers the default decorators:

<?php
$this
->addDecorators(array(
    array(
'ViewHelper'),
    array(
'Errors'),
    array(
'Description', array('tag' => 'p''class' => 'description')),
    array(
'HtmlTag', array('tag' => 'dd')),
    array(
'Label', array('tag' => 'dt')),
));

The initial content is created by the 'ViewHelper' decorator, which creates the form element itself. Next, the 'Errors' decorator fetches error messages from the element, and, if any are present, passes them to the 'FormErrors' view helper to render. If a description is present, the 'Description' decorator will append a paragraph of class 'description' containing the descriptive text to the aggregated content. The next decorator, 'HtmlTag', wraps the element, errors, and description in an HTML <dd> tag. Finally, the last decorator, 'label', retrieves the element's label and passes it to the 'FormLabel' view helper, wrapping it in an HTML <dt> tag; the value is prepended to the content by default. The resulting output looks basically like this:

<dt><label for="foo" class="optional">Foo</label></dt>
<dd>
    <input type="text" name="foo" id="foo" value="123" />
    <ul class="errors">
        <li>"123" is not an alphanumeric value</li>
    </ul>
    <p class="description">
        This is some descriptive text regarding the element.
    </p>
</dd>

For more information on decorators, read the Zend_Form_Decorator section.

Using Multiple Decorators of the Same Type

Internally, Zend_Form_Element uses a decorator's class as the lookup mechanism when retrieving decorators. As a result, you cannot register multiple decorators of the same type; subsequent decorators will simply overwrite those that existed before.

To get around this, you can use aliases. Instead of passing a decorator or decorator name as the first argument to addDecorator(), pass an array with a single element, with the alias pointing to the decorator object or name:

<?php
// Alias to 'FooBar':
$element->addDecorator(array('FooBar' => 'HtmlTag'),
                       array(
'tag' => 'div'));

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

In the addDecorators() and setDecorators() methods, you will need to pass the 'decorator' option in the array representing the decorator:

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

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

Methods associated with decorators include:

  • addDecorator($nameOrDecorator, array $options = null)

  • addDecorators(array $decorators)

  • setDecorators(array $decorators) (overwrites all decorators)

  • getDecorator($name) (retrieve a decorator object by name)

  • getDecorators() (retrieve all decorators)

  • removeDecorator($name) (remove decorator by name)

  • clearDecorators() (remove all decorators)

Zend_Form_Element also uses overloading to allow rendering specific decorators. __call() will intercept methods that lead with the text 'render' and use the remainder of the method name to lookup a decorator; if found, it will then render that single decorator. Any arguments passed to the method call will be used as content to pass to the decorator's render() method. As an example:

<?php
// Render only the ViewHelper decorator:
echo $element->renderViewHelper();

// Render only the HtmlTag decorator, passing in content:
echo $element->renderHtmlTag("This is the html tag content");

If the decorator does not exist, an exception is raised.

Zend Framework