Skip to content

My favorite Zend_Form decorators configuration

2009 June 15
by Richard Knop

In the previous post I talked about extending Zend_Form. At the end of the post I shortly mentioned decorators and what they do.

The default markup they produce is a definition list. But many web designers (incuding me) prefer some other markup. My favorite XHTML for forms is presented in the Prettier Accessible Forms article at A List Apart. It uses an ordered list instead of a definition list. Here is how to achieve it with Zend_Form decorators (the form is the same as in the last post):

  1. class Contact extends Zend_Form
  2. {
  3.     private $elementDecorators = array(
  4.         'ViewHelper',
  5.         array(array('data' => 'HtmlTag'), array('tag' => 'div', 'class' => 'element')),
  6.         'Label',
  7.         array(array('row' => 'HtmlTag'), array('tag' => 'li')),
  8.     );
  9.  
  10.     private $buttonDecorators = array(
  11.         'ViewHelper',
  12.         array(array('data' => 'HtmlTag'), array('tag' => 'div', 'class' => 'button')),
  13.         array(array('row' => 'HtmlTag'), array('tag' => 'li')),
  14.     );
  15.  
  16.     private $captchaDecorators = array(
  17.         'Label',
  18.         array(array('row' => 'HtmlTag'), array('tag' => 'li'))
  19.     );
  20.  
  21.     public function init()
  22.     {
  23.         $this->setMethod('post');
  24.  
  25.         $name = new Zend_Form_Element_Text('name', array(
  26.             'decorators' => $this->elementDecorators,
  27.             'label' => 'Your name',
  28.             'required' => true,
  29.             'filters' => array(
  30.                 'StringTrim'
  31.             ),
  32.             'validators' => array(
  33.                 array('StringLength', false, array(3, 50))
  34.             ),
  35.             'class' => 'input-text'
  36.         ));
  37.  
  38.         $email = new Zend_Form_Element_Text('email', array(
  39.             'decorators' => $this->elementDecorators,
  40.             'label' => 'Your email',
  41.             'required' => true,
  42.             'filters' => array(
  43.                 'StringTrim'
  44.             ),
  45.             'validators' => array(
  46.                 'EmailAddress'
  47.             ),
  48.             'class' => 'input-text'
  49.         ));
  50.  
  51.         $subject = new Zend_Form_Element_Text('subject', array(
  52.             'decorators' => $this->elementDecorators,
  53.             'label' => 'Subject',
  54.             'required' => true,
  55.             'filters' => array(
  56.                 'StringTrim'
  57.             ),
  58.             'validators' => array(
  59.                 array('StringLength', false, array(3, 50))
  60.             ),
  61.             'class' => 'input-text'
  62.         ));
  63.  
  64.         $message = new Zend_Form_Element_Textarea('message', array(
  65.             'decorators' => $this->elementDecorators,
  66.             'label' => 'Message',
  67.             'rows' => 10,
  68.             'cols' => 50,
  69.             'required' => true,
  70.             'filters' => array(
  71.                 'StringTrim'
  72.             ),
  73.             'validators' => array(
  74.                 array('StringLength', false, array(20, 1500))
  75.             )
  76.         ));
  77.  
  78.         $captcha = new Zend_Form_Element_Captcha('captcha', array(
  79.             'decorators' => $this->captchaDecorators,
  80.             'label' => 'Are you a human?',
  81.             'helper' => null,
  82.             'captcha' => array(
  83.                 'captcha' => 'Figlet',
  84.                 'wordLen' => 6,
  85.             ),
  86.             'class' => 'input-text'
  87.         ));
  88.  
  89.         $submit = new Zend_Form_Element_Submit('contact', array(
  90.             'decorators' => $this->buttonDecorators,
  91.             'label' => 'Submit',
  92.             'class' => 'input-submit'
  93.         ));
  94.  
  95.         $this->addElements(array($name, $email, $subject, $message, $captcha, $submit));
  96.     }
  97.  
  98.     public function loadDefaultDecorators()
  99.     {
  100.         $this->setDecorators(array(
  101.             'FormErrors',
  102.             'FormElements',
  103.             array('HtmlTag', array('tag' => 'ol')),
  104.             'Form'
  105.         ));
  106.     }
  107. }

Standard decorators for form elements are:

  • ViewHelper (the element tag, for example <input> or <textarea>)
  • Errors (errors, by default in <ul>)
  • HtmlTag (<dd> by default)
  • Label (wrapped in <dt> by default)

Standard decorators for the form are:

  • FormElements (iterates through form elements)
  • HtmlTag (<dl> by default)
  • Form (<form>)

I use another decorator to display form errors all on top of the form:

  • FormErrors

In the form class above I have defined customized decorators for some form elements. In case there were checkboxes or radio buttons in the form, I would add these decorators:

  1. private $checkboxDecorators = array(
  2.     'Label',
  3.     'ViewHelper',
  4.     array(array('data' => 'HtmlTag'), array('tag' => 'div', 'class' => 'checkbox')),
  5.     array(array('row' => 'HtmlTag'), array('tag' => 'li')),
  6. );
  7.  
  8. private $radioDecorators = array(
  9.     'Label',
  10.     'ViewHelper',
  11.     array(array('data' => 'HtmlTag'), array('tag' => 'div', 'class' => 'radio')),
  12.     array(array('row' => 'HtmlTag'), array('tag' => 'li')),
  13. );

The loadDefaultDecorators() method applies custom decorators to the form object. The decorators are rendered from inside in the order they are specified. So it works like this:

  1. FormErrors is rendered.
  2. FormElements is rendered.
  3. The <ol> tag is rendered.
  4. Finally, the <form> tag is rendered (wrapping all other content inside it).

In conclusion, every decorator renders only its own content and the final form consists of all decorators clumped together.

6 Responses leave one →
  1. KonstantinMiller permalink
    July 6, 2009

    Hi. I like the way you write. Will you post some more articles?

  2. July 7, 2009

    Sure I will write new posts in the future. I’m in the middle of something (larger project) right now though so I don’t have much time.

  3. BBMAN permalink
    August 17, 2009

    You don’t want to set decorators on EACH element of EACH your form, it is much better to extend Zend_Form as My_Form and set your decorators there ONCE and forget about the annoying dl dt dd tags forever

  4. January 8, 2010

    Hi Richard,

    Nice to see others also have a handy way to change the markup for those standard elements. Have you found a way to change the markup for the Errors element. I want to but can’t seem to find it without having to do so through the elements view->getHelper(‘formErrors’)

    I also came up with something similar with which you don’t need to add the decorators in your definition at all. But should you wish to you still can.

    I also have some example code up for download. Check my post about this subject at http://www.sreknord.net/blog/zend-framework/configure-zend-framework-standard-form-decorators

  5. February 4, 2012

    Richard, good work and thanks for sharing. What I was looking for.

    I want to address BBMan comment about extending Zend_Form: doing so is not obvious. You have to jump through some hoops to get all the Zend decorator stuff cooperating: defaults, element overrides, stack manipulation, etc.

    I’ve documented the plug-in strategy I took. Richard’s classes are expressed in an Accessible_Form class that you can drop-in:
    http://www.ideacode.com/content/plug-in-custom-Zend-Form-decorators

Trackbacks and Pingbacks

  1. User login and authentication with Zend_Auth and Zend_Acl | Richard Knop’s Blog

Leave a Reply

Note: You can use basic XHTML in your comments. Your email address will never be published.

Subscribe to this comment feed via RSS