Configure Zend Framework standard Form Decorators

Wednesday, January 6, 2010

Related download: Configure standard Zend Form Decorators

After having ‘attended’ the Zend webinar about Zend Form Decorators I wasn’t quite sure about what I had just learned. I was just looking  for an easy way to setup up my decorators for the forms I use, site-wide or on a per-module basis. As I am happy with the existing decorators and the need to write my own hasn’t presented itself yet.

In this post I write about using the standard decorators and how to configure them in an easy way without having to add them in your element definitions but with keeping the possibility to do so. As there are enough post about Zend Form and its decorators I will not go into how to write your own decorators and how to use then in your forms. I have made the assumption that the reader understands how decorators work and how their output creates what we know as the Zend_Form output.

What am I thinking:

  • I use forms(Zend_Form) but think that decorating them is part of the application or module that they are used in.
  • I want to be able to control the standard decorators to decorate forms and elements within those forms.
  • I want to keep existing behavior.
  • I use css: I just need some properly named selectors for the most part.

Theory of operation

Just to get our heads around what all is involved to get those standard decorators under control we have to know how the Zend Form works and where and how decorators are used. The default form decorators are loaded after instantiation through the usage of loadDefaultDecorators() in the following classes:

  • Zend_Form
  • Zend_Form_DisplayGroup
  • Zend_Form_SubForm
  • Zend_Form_Element
  • Zend_Form_Element_Button
  • Zend_Form_Element_Captcha
  • Zend_Form_Element_Checkbox
  • Zend_Form_Element_File
  • Zend_Form_Element_Hash
  • Zend_Form_Element_Hidden
  • Zend_Form_Element_Image
  • Zend_Form_Element_MultiCheckbox
  • Zend_Form_Element_MultiSelect
  • Zend_Form_Element_Password
  • Zend_Form_Element_Radio
  • Zend_Form_Element_Reset
  • Zend_Form_Element_Select
  • Zend_Form_Element_Submit
  • Zend_Form_Element_Text
  • Zend_Form_Element_TextArea

For me this was the hard part, to find a uniform way to change the loaded defaults and keep existing functionality. I decided to approach it in two ways. One is to set these defaults through the use of Zend_Config_Ini, and another way through the usage of class properties. The latter is my least favorite because you would have to get into the code to change the settings but It could be favored by others since no separate ini file parsing is needed. I will explain the first below, both are available through the related dowload.

Sub classing Zend_Form

Sub classing Zend Form was inevitable as I wanted to override the default decorators loaded. As you see for certain form element classes; they override the loadDefaultDecorators method to set their own defaults. Have a look at for example the Zend_Form_Element_Submit, here you can see that not all elements load the same decorators. My goal though was to keep existing behavior, I don’t want to subclass all the Zend_Form_Element classes and change their defaults by overriding the loadDefaultDecorators() method (Which is actually yet another option to work part of this problem). 

What I ended up doing is overriding the Zend Form constructor and the addElement method. In the constructor I parse the ini file which contains my default -per element- setting such as the one below.

Example config element

[button]
tooltip.decorator = "ToolTip"
viewhelper.decorator = "ViewHelper"
htmltag.decorator = "HtmlTag"
htmltag.options.tag = "div"

In the constructor I also set the form’s decorators, then in the overridden addElement I have added the new functionality as displayed below.

    /**
     * Constructor
     *
     * @param mixed $options
     * @return void
     * @todo add file_exists, and some validation of its content
     */
    public function __construct($options = null)
    {
        if(defined('FORM_DECORATORS_INI')){
            
            $ini = new Zend_Config_Ini(FORM_DECORATORS_INI);
            $this->_decoratorSettings = $ini->toArray();
            
            // set decorators for the form itself
            $this->setDisableLoadDefaultDecorators(true);
            $this->setDecorators($this->_getDecoratorSettings('form'));
        }
        
        // instantiate the parent class
        parent::__construct($options);
    }
    /**
     * Add an element to this form
     *
     * Uses $_decoratorSettings when set for the $element
     * and the $options array does not contain them
     *
     * @see Zend_Form#addElement($element, $name, $options)
     */
    public function addElement($element, $name = null, $options = null)
    {
        // get decorators from ini if not set in definition
        if(false === isset($options['decorators'])
            && null !== $this->_decoratorSettings)
        {
            $decoratorSettings = array();
            $decoratorSettings['decorators'] = $this->_getDecoratorSettings($element);
            $options = (is_array($options) ? $options : array());
            $options = array_merge($options, $decoratorSettings);
        }
        
        // call parent to do adding
        parent::addElement($element, $name, $options);
    }

That’s basically it. Now I can use addElement as I am used to and define my forms as desired. For we will always have to define our forms but now I don’t have to fuss with the appearance of the elements trough setting of the decorators in the options, which I think don’t belong in the form definition. But If I want to define them, I can. So even if I want to set different decorators than the ones I defined as default I can.

        // add element without having to set decorators
$this->addElement('text','textinput1',array(
            'label'        => 'textinput 1',
            'required'  => true
        ));

This solution has proven to work for me in most cases. I know I will -at some point- have to write my own decorators or subclass or create new form elements. But for now this is a very workable solution.

Related download: Configure standard Zend Form Decorators

Comments

Marcel Koonstra says:
Friday, April 16, 2010
Great Idea is this, I was looking for something like this for myself. So I have taken your source and extended it a little. Instead of requiring a FORM_DECORATORS_INI constant I have added the config as an option to the constructor. Furthermore I have extended the functionality to Display Groups and also added support for specific field overrides by name. If you are interested, please contact me.
George says:
Thursday, June 10, 2010
Marcel, you're almost there. A better refactoring would put the config file -path and/or -name in the options array. Then check for those option values and remove them before passing the options to the parent.

Leave a reply

 
Please type this word backwards: marih
 
 
 

Blog categories

Zend-Framework Databases Php

About

Sreknord.net is the personal web space of software engineer, Leonard Dronkers (NL). At Sreknord.net you will find interesting information about software engineering and web-development.

Leonard Dronkers

Software engineer, Leonard Dronkers is currently working as a web-developer at VNU Media (NL).

Professional interests:

To get in touch with Leonard, please use the contact form.