Overriding Zend form element default decorators, for good!
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') ); |


November 6th, 2009 at 6:23 pm
Nice.
You can set group class with
$this->setDefaultDisplayGroupClass(‘Tolerable_Form_DisplayGroup’);
instead having attribute in extended class.
November 7th, 2009 at 9:11 am
@umpirsky Does the same thing.
November 20th, 2009 at 6:52 pm
@Phill Is there a way to override element decorators without touching zend classes (abstract class Zend_Form_Element_Xhtml extends Tolerable_Form_Element), and not extending every Zend_Form_Element_* class, but keep control over decorator id’s depending on element names?
Question is explained here http://old.nabble.com/Override-Zend_Form_Element-decorators-td26421495.html#a26421495
March 13th, 2010 at 10:23 pm
That’s exactly what I’ve been looking for.. Thanks for sharing.