Zend_Form decorator tips

August 7, 2008 – 2:56 pm Tags:

It seems a lot of people are struggling with Zend_Form decorators. They want to know how to modify the markup, but they can’t figure out how to do it with the bundled decorators or how to write their own.

Since I’ve done some decorator-stuff myself, I thought I’d share some tips on making the output a bit nicer. For example, how to make Zend_Form use <ul> and <li> tags instead of <dd>, <dt> and <dl>. I also wanted some inputs appear side by side (date from and date to in my case).

General tips

Learn what the bundled decorators do and which are required. You can do a lot of stuff with the decorators that are shipped with Zend Framework, for example the HtmlTag decorator can be used in most scenarios.

Also, learn the vital decorators: For forms, you’ll usually need Form and FormElements, for DisplayGroups FormElements and FieldSet and for elements, ViewHelper, Label and Errors. Without these decorators, the form won’t work very well – you could always make your own, but most of the time these basic ones should suffice.

With the decorators I listed above, you will get a very basic form; basically just the form element, the labels and the inputs – no dd, dt or such. From there, it’s easy to start building your own markup, which I will show later in this post.

The HtmlTag decorator

Let’s take a quick look at the HtmlTag decorator, since it’s very useful.

You can use it to wrap any element, or form, in a specific HTML tag, such as a div. You can also use it to wrap multiple elements inside a single element with some smart usage of the parameters.

If you have two elements that you wish to put inside a div, you can do the following:

//add two elements
$form->addElement('text', 'one');
$form->addElement('text', 'two');
 
//Prepend an opening div tag before "one" element:
$form->one->addDecorator('HtmlTag', array(
    'tag' => 'div',
    'openOnly' => true,
    'placement' => Zend_Form_Decorator_Abstract::PREPEND
));
 
//Append a closing div tag after "two" element:
$form->two->addDecorator('HtmlTag', array(
    'tag' => 'div',
    'closeOnly' => true,
    'placement' => Zend_Form_Decorator_Abstract::APPEND
));
 
//The markup for one and two would now appear inside a <div> </div> block

As you can see, it’s actually quite simple to customize the markup a bit with the HtmlTag.

Custom decorators

Creating custom decorators is also quite simple. Just create a new class which extends Zend_Form_Decorator_Abstract, or implements Zend_Form_Decorator_Interface. Generally speaking, extending Decorator_Abstract is easier since you’ll have less things to code.

When extending Zend_Form_Decorator_Abstract, all you need to do is to define a method called render:

/**
 * This example decorator wraps things in div blocks
 */
class MyDecorator extends Zend_Form_Decorator_Abstract
{
    public function render($content)
    {
        return '<div>' . $content . '</div>';
    }
}

Adding this decorator to a form element would achieve the same effect as adding a HtmlTag decorator with div tag, so it’s perhaps a bit pointless, but serves as an example.

Next, we’ll look at changing the form output from dl/dd/dt to ul/li, and we’ll also use one decorator with DisplayGroups: CU_Form_Decorator_AllErrors, which grabs all child element errors and displays them.

Adding it all together: Modifying form output

Now that we know how decorators work and all that, let’s apply it to reach the ultimate goal: Changing Zend_Form’s default dd/dt/dl based markup to ul/li based one which is not only lighter but easier to style (in my opinion).

First, we’ll need a form:

  • Your name

    Errors for both name fields would be here

<style type="text/css">
#exampleform { list-style: none; margin: 0; padding: 0; }
#exampleform fieldset { border: none; margin: 0; padding: 0; }
#exampleform fieldset div { width: 100px; float: left; }
#exampleform label {display: block; }
#exampleform input { width: 100px; }
#exampleform p { clear: both }
</style>
<ul id="exampleform">
 <li>
  <fieldset>
   <legend>Your name</legend>
   <div>
    <input type="text" />
   </div>
   <div>
    <input type="text" />
   </div>
  </fieldset>
 </li>
 <li>
  <label for="phone">Phone</label>
  <input type="text" id="phone" />
 </li>
 <li>
  <input type="submit" value="ok" />
 </li>
</ul>

So the end result we want is something like the form above, with the markup looking somewhat like that as well, but not with that style-block obviously. I just included it for completeness’ sake, in case you were wondering how to make the markup look like that.

Let’s create a form class that we can use:

class ExampleForm extends Zend_Form
{
    public function init()
    {
        //We don't want the default decorators
        $this->setDisableLoadDefaultDecorators(true);
 
        $this->addDecorator('FormElements')
             ->addDecorator('HtmlTag', array('tag' => 'ul') //this adds a <ul> inside the <form>
             ->addDecorator('Form');
 
        //Create the name elements
        $this->addElement('text', 'firstname', array(
            'required' => true
        ));
 
        $this->addElement('text', 'lastname', array(
            'required' => true
        ));
 
        //Put the elements inside a displaygroup to get the fieldset and all
        $this->addDisplayGroup(array('firstname', 'lastname'), 'name', array(
            'legend' => 'Your name'
        ));
 
        $this->addElement('text', 'phone', array(
            'label' => 'Phone'
        ));
 
        $this->addElement('submit', 'ok', array(
            'label' => 'OK'
        ));
 
        //Set the decorators we need:
        $this->setElementDecorators(array(
            'ViewHelper',
            'Label',
            'Errors',
            new Zend_Form_Decorator_HtmlTag(array('tag' => 'li')) //wrap elements in <li>'s
        ));
 
        //Set decorators for the name fields so they appear side by side
        $this->setElementDecorators(array(
            'ViewHelper',
            'Label',
            new Zend_Form_Decorator_HtmlTag(array('tag' => 'div')) //wrap names in <div> for the float
        ), array('firstname', 'lastname'));
 
        //Set decorators for the displaygroup:
        $this->setDisplayGroupDecorators(array(
            'FormElements',
            'Fieldset',
            new CU_Form_Decorator_AllErrors(),
            new Zend_Form_Decorator_HtmlTag(array('tag' => 'li')) //wrap groups in <li>'s too
        ));
 
        //Remove label from submit button:
        $this->ok->removeDecorator('Label');
    }
}

And there it is. If you render the above form, you should get very similar markup to what I used to create the example. Apply the CSS, and it should look quite the same as well.

There’s one thing in the above code that may need a bit explaining: You can see we add the Errors decorator to the elements, but not on firstname and lastname. This is because the CU_Form_Decorator_AllErrors decorator we add to the DisplayGroups will take care of rending the errors.

And in case you missed it, you can get CU_Form_Decorator_AllErrors from the usual place.

In closing

Despite seeming overly complex and confusing, the decorators in Zend_Form are very powerful and offer lots of flexibility. They’re also quite simple to use after you understand the concept.

More on Zend_Form:
Complex custom elements in Zend_Form
Autogenerating forms from Doctrine models
Client side validation with Zend_Form

Share this:

RSS feed Subscribe to my RSS feed

About the author

Jani is a 15 year veteran of the software industry. He's currently available for consulting

  1. 25 Responses to “Zend_Form decorator tips”

  2. Nice tutorial. I think that you missed the Phone field in the Example ZF code.

    By Juan on Aug 8, 2008

  3. Oops, how did that happen? Fixed now. Thanks for spotting it =)

    By Jani Hartikainen on Aug 8, 2008

  4. Your tutorials rule! :) Seriously, you’ve got great tutorials for coders on your site. Just keep up the good work!

    Miladin

    By Miladin on Aug 8, 2008

  5. You shouldn’t use an unordered list for a form. You should use labels. If for some messed up reasons the labels aren’t satisfactory, you should use a definition list just like Zend does out of the box. What’s what those tags are there for.

    By Bram on Aug 14, 2008

  6. If <label> isn’t a label, I don’t know what it is =)

    By Jani Hartikainen on Aug 15, 2008

  7. Great Tutorial! Good explanation etc.

    For ZF beginners its clearly to understand!

    By Aleks on Aug 19, 2008

  8. Thanks Jani,

    Form decorators was something I struggled with to begin with and this has help me gain a better understanding.

    By Tom Graham on Aug 25, 2008

  9. Great tutorial. Sweet and concise.

    By Jameel Khan on Oct 12, 2008

  10. Thanks a lot for your effort. I was in trouble with coding Zend decorators until i came to your post. For me these explainings above show me a good solution. oOo

    By Guido on Oct 20, 2008

  11. Very nice tutorial.

    Now I’m trying to wrap the label and input elements in a div. I mean, how could I get the following html code?

    Label for the input

    I tried adding a new decorator instance between the viewhelper and the label but didn’t work.

    Thank in advanced for you reply.

    By Saul Martínez on Jan 4, 2009

  12. Oops… cannot write html code.

    well. the html code is the next:

    ul
    li
    div
    label
    /div
    div
    input
    /div
    /li
    /ul

    By Saul Martínez on Jan 4, 2009

  13. Easy way ;- )


    class My_Form extends Zend_Form
    {
    public function addElement($element, $name = null, $options = null)
    {
    if ( is_string('element') )
    {
    $element = $this->createElement($element, $name, $options);
    $element->removeDecorator('DtDdWrapper');
    $element->removeDecorator('Label');
    $element->addDecorator('HtmlTag', array('tag' => 'div', 'class' => 'zend_form'));
    $element->addDecorator('Label', array('tag' => 'div', 'class' => 'zend_form'));

    }
    parent::addElement($element);
    }
    }

    By Erik Seifert on May 28, 2009

  14. Nice tutorial, I found it really helpful!

    I did spot one missing close bracket on this line

    ->addDecorator(‘HtmlTag’, array(‘tag’ => ‘ul’) //this adds a inside the

    By Sophia on Jun 11, 2009

  15. Can you do the APPEND | PREPEND w/ individual tags surrounding a form element using a Zend_Config object through the .ini file? If so, how? :)

    Great article btw!

    By Joe Devon on Jun 20, 2009

  16. Thank you very much, this tips were a great help for me and safed me from a lot of unnecessary work =D!

    By David on Jun 20, 2010

  17. Wow, 1 year exactly without a comment :)

    By Saul Martínez on Jun 21, 2010

  18. That’s a good article.
    If anyone is interested in have a look my examples, I recommend you have a look the following link:

    http://javierbracero.blogspot.com/2011/03/zend-decorators.html

    By Javier on Mar 14, 2011

  19. Thank you so much for sharing this. You saved me tons of time :)

    By Enrique on Mar 15, 2012

  20. You’re right, .. developers are struggling with decorators because it’s pointless, .. I mean, there are good once you know how to use them but if you’re just starting with the platform, it makes it harder to do the simplest thing, .. they should be optional not the defacto of using forms in the framework

    By Adnan on Mar 22, 2012

  21. I entirely agree with Adnan on this. Who ever came up with decorators needs to be dragged out and shot.

    We already have views for holding layout logic but using a viewscript for each form element is not working when used in conjuction with drop down selects.

    By Ash Vince on Aug 1, 2012

  22. Thank you thank you thank you so much. GREAT post containing exactly what I was looking for summed up very succinctly!!!

    To those complaining about view data here… most decent coders these days use css tools for most display manipulation. In order to do this properly one needs html tags with proper identifiers and this accomplishes that nicely.

    This way one can use Zend built-in stuff like validation/stripslashes/etc AND display very easily in multiple places with minimal code.

    Best way to minimal-zend it imo.

    By ken on Mar 15, 2013

  1. 4 Trackback(s)

  2. Oct 23, 2008: Complex custom elements in Zend_Form | CodeUtopia
  3. Oct 31, 2008: » Book review: The Art and Science of CSS - internet development - strictlyPHP
  4. Mar 5, 2009: Stock decorators « mzBlog
  5. Mar 10, 2009: Database backed Zend_Form elements | CodeUtopia

Post a Comment

You can use some HTML (a, em, strong, etc.). If you want to post code, use <pre lang="PHP">code here</pre> (you can replace PHP with the language you are posting)