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.

Share this:
  • Digg
  • del.icio.us
  • Facebook
  • Google
  • description
  • E-mail this story to a friend!
  • LinkedIn
  • Pownce
  • Reddit
  • StumbleUpon
  • Technorati
RSS feed:
Subscribe!
  1. 11 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

  1. 2 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

Post a Comment