I’m sure everybody who has used Zend_Form has, at one time or another, wished they could change the default form element decorators in one fell swoop, simply and efficiently.

Up until now, I’ve been using one of two methods to override the default “definition list” style; set each element’s decorator scheme on instantiation or, use the form’s “setElementDecorators” method to reset the scheme for nominated elements. No longer!

Using just a few simple classes, I’ll show you how to permanently alter the default decorator scheme, thus simplifying your code and making it ultimately more flexible.

For the following example, I’ll assume a decorator scheme like the following

form
  fieldset
    ol
      li
        label
        element
      li
        button

Lets start by extending Zend_Form_Element. I’d also like to take this opportunity to introduce my new “Tolerable” library.

// library/Tolerable/Form/Element.php
 
class Tolerable_Form_Element extends Zend_Form_Element
{
    /**
     * Load default decorators
     *
     * @return void
     */
    public function loadDefaultDecorators()
    {
        if ($this->loadDefaultDecoratorsIsDisabled()) {
            return;
        }
 
        $decorators = $this->getDecorators();
        if (empty($decorators)) {
            $this->addDecorator('ViewHelper')
                 ->addDecorator('Errors')
                 ->addDecorator('Description', array('tag' => 'p',
                                                     'class' => 'description'))
                 ->addDecorator('Label', array('requiredSuffix' => ' *'))
                 ->addDecorator('HtmlTag', array('tag' => 'li',
                                                 'id'  => $this->getName() . '-element'));
        }
    }
}

Don’t forget to add your library namespace to the auto loader

; application/configs/application.ini
 
autoloaderNamespaces.1 = "Tolerable_"

Zend_Form_Element_Submit also needs some treatment

// library/Tolerable/Form/Element/Submit.php
 
class Tolerable_Form_Element_Submit extends Zend_Form_Element_Submit
{
    /**
     * Default decorators
     *
     * Uses only 'Submit' and 'li' decorators by default.
     * 
     * @return void
     */
    public function loadDefaultDecorators()
    {
        if ($this->loadDefaultDecoratorsIsDisabled()) {
            return;
        }
 
        $decorators = $this->getDecorators();
        if (empty($decorators)) {
            $this->addDecorator('Tooltip')
                 ->addDecorator('ViewHelper')
                 ->addDecorator('HtmlTag', array('tag' => 'li'));
        }
    }
}

We’ll also need to provide our own display group class to support the desired scheme

// library/Tolerable/Form/DisplayGroup.php
 
class Tolerable_Form_DisplayGroup extends Zend_Form_DisplayGroup
{
    /**
     * Load default decorators
     * 
     * @return void
     */
    public function loadDefaultDecorators()
    {
        if ($this->loadDefaultDecoratorsIsDisabled()) {
            return;
        }
 
        $decorators = $this->getDecorators();
        if (empty($decorators)) {
            $this->addDecorator('Description', array('tag'   => 'li',
                                                     'class' => 'group_description'))
                 ->addDecorator('FormElements')
                 ->addDecorator('HtmlTag', array('tag' => 'ol'))
                 ->addDecorator('Fieldset');
        }
    }
}

as well as a custom form class

// library/Tolerable/Form.php
 
class Tolerable_Form extends Zend_Form
{
    /**
     * Default display group class
     * @var string
     */
    protected $_defaultDisplayGroupClass = 'Tolerable_Form_DisplayGroup';
 
    /**
     * Constructor
     *
     * Add custom prefix path before parent constructor
     *
     * @param mixed $options
     * @return void
     */
    public function __construct($options = null)
    {
        $this->addPrefixPath('Tolerable_Form', 'Tolerable/Form');
        parent::__construct($options);
    }
 
    /**
     * Load the default decorators
     *
     * @return void
     */
    public function loadDefaultDecorators()
    {
        if ($this->loadDefaultDecoratorsIsDisabled()) {
            return;
        }
 
        $decorators = $this->getDecorators();
        if (empty($decorators)) {
            $this->addDecorator('FormElements')
                 ->addDecorator('Form');
        }
    }
}

Now that we’ve laid the way for our custom decorator scheme, there’s only one item remaining and I can’t say that I’m proud but hey, it works.

All front facing (HTML) Zend form elements extend the empty, abstract class Zend_Form_Element_Xhtml. We’re going to override this class in its current form. This method works well if the Zend Framework is not in the application’s “library” which should have a higher “include path” setting than the framework itself. Otherwise, you’ll just have to override the existing Zend Framework file.

// library/Zend/Form/Element/Xhtml.php
 
abstract class Zend_Form_Element_Xhtml extends Tolerable_Form_Element
{
}

That’s it. Get ready to start writing leaner, meaner forms

$form = new Tolerable_Form;
$form->addElement('text', 'foo', array(
    'label'    => 'Foo',
    'required' => true
))->addElement('textarea', 'bar', array(
    'label'    => 'Bar'
))->addElement('submit', 'submit_btn', array(
    'label'    => 'Submit'
))->addDisplayGroup(
    array('foo', 'bar', 'submit_btn'),
    'baz',
    array('legend' => 'My Form')
);