Christer’s blog o’ fun

April 25, 2008

Quick and dirty custom Zend_Form_Decorator_Label

Filed under: PHP, Technology — Tags: , , , — christer @ 5:04 pm

Someone on the zf-mvc list asked the following question:

how to add elements like the description between label and input, for example:


<dt>
    <label>Your E-Mail: *</label><span class="Description">- won't be published</span>
</dt>
<dd>
    <input value="" id="email" class="" type="text" />
</dd>

This is currently not possible out of the box with Zend_Form. I put on my problem-solving-hat and quickly made a rather dirty implementation of a custom decorator to solve the problem specified above. I made a class called My_Form_Decorator_LabelWithDescription that extends the Zend_Form_Decorator_Label class. My custom label class looks like this:


<?php
require_once 'Zend/Form/Decorator/Label.php';

class My_Form_Decorator_LabelWithDescription extends Zend_Form_Decorator_Label {
    /**
     * Description decorator
     *
     * @var Zend_Form_Decorator_Description
     */
    protected $_description = null;

    /**
     * Set the description decorator
     *
     * @param Zend_Form_Decorator_Description $description
     * @return My_Form_Decorator_LabelWithDescription
     */
    public function setDescription(Zend_Form_Decorator_Description $description) {
        $this->_description = $description;
        return $this;
    }

    /**
     * Get the description decorator
     *
     * @return Zend_Form_Decorator_Description
     */
    public function getDescription() {
        return $this->_description;
    }

    public function render($content) {
        $element = $this->getElement();
        $view    = $element->getView();
        if (null === $view) {
            return $content;
        }

        $label     = $this->getLabel();
        $separator = $this->getSeparator();
        $placement = $this->getPlacement();
        $tag       = $this->getTag();
        $id        = $this->getId();
        $class     = $this->getClass();
        $options   = $this->getOptions();

        if (empty($label) &amp;&amp; empty($tag)) {
            return $content;
        }

        if (!empty($label)) {
            $options['class'] = $class;
            $label = $view->formLabel($element->getName(), trim($label), $options);
        }

        if (null !== $this->_description) {
            $this->_description->setElement($element);
            $label = $this->_description->render($label);
        }

        if (null !== $tag) {
            require_once 'Zend/Form/Decorator/HtmlTag.php';
            $decorator = new Zend_Form_Decorator_HtmlTag();
            $decorator->setOptions(array('tag' => $tag));
            $label = $decorator->render($label);
        }

        switch ($placement) {
            case self::APPEND:
                return $content . $separator . $label;
            case self::PREPEND:
                return $label . $separator . $content;
        }
    }
}

I have added a protected $_description property and a set/get method for it.

The render method is simply copied from the parent class with a small modification:


if (null !== $this->_description) {
    $this->_description->setElement($element);
    $label = $this->_description->render($label);
}

This will render the description and add it to the label.

The last part of this post will show you how to enable this marvelous new decorator. I will use some code from my Translating Zend_Form error messages and more post, so if you haven’t read that yet you should catch up by doing so. :)

In the mentioned post I had some custom elements that added decorators. Lets add this new decorator to the custom text fields. The My_Form_Element_Text class looked like this:


<?php
require_once 'Zend/Form/Element/Text.php';

class My_Form_Element_Text extends Zend_Form_Element_Text {
    public function init() {
        $this->setDecorators(array(
             array('ViewHelper'),
             array('Description', array('escape' => false,
                                        'class' => 'fieldDescription')),
             array('Errors'),
             array('HtmlTag', array('tag' => 'dd')),
             array('Label', array('requiredSuffix' => ' *',
                                  'tag' => 'dt',
                                  'escape' => false))
        ));
    }
}

As you can see both the Label and Description decorators are added, but since my decorator includes the Description within the Label we don’t need both! After a little modification, the class now looks like:


<?php
require_once 'Zend/Form/Element/Text.php';
require_once 'Zend/Form/Decorator/Description.php';
require_once 'My/Form/Decorator/LabelWithDescription.php';

class My_Form_Element_Text extends Zend_Form_Element_Text {
    public function init() {
        $description = new Zend_Form_Decorator_Description(array(
            'tag' => 'span',
            'escape' => false,
            'class' => 'fieldDescription'));

        $label = new My_Form_Decorator_LabelWithDescription(array(
            'requiredSuffix' => ' *',
            'tag' => 'dt',
            'escape' => false));

        // Add the description decorator to the label
        $label->setDescription($description);

        $this->setDecorators(array(
             array('ViewHelper'),
             array('Errors'),
             array('HtmlTag', array('tag' => 'dd')),
             array($label)
        ));
    }
}

And voila! You now have a fully customizable Description decorator that will be rendered inside the Label. Pretty neat eh?

This is not exactly the best implementation of such a decorator but it gets the job done. It would be cool if some hooks where added to the default decorator that child classes could implement. That way I wouldn’t have to copy the entire render method from the Label decorator.

4 Comments »

  1. :)
    Thanks for your work man, that’s exactly what i’m searching for.

    Dennis

    Comment by Dennis Winter — April 25, 2008 @ 5:20 pm

  2. Christer Puts on His Problem Solver Hat…

    I’m not sure how much sugar Christer got into his system today, but he’s been completely on fire with his blog posts. This one shows how to write a custom Zend_Form_Decorator_Label, which he wrote after someone asked a question about how …

    Trackback by Mats Lindh — April 25, 2008 @ 7:22 pm

  3. [...] into his system today, but he’s been completely on fire with his blog posts. This one shows how to write a custom Zend_Form_Decorator_Label , which he wrote after someone asked a question about how to do something in particular on the Zend [...]

    Pingback by Christer Puts on His Problem Solver Hat | Mats Lindh — April 29, 2008 @ 11:21 pm

  4. [...] objects with Zend_View’s PartialLoop helper. I continued the Zend Framework blogging with the Quick and dirty custom Zend_Form_Decorator_Label post and ended the month with a post on how to use a SOCKS proxy to connect to MSN Messenger [...]

    Pingback by 2008 is almost at an end « Christer’s blog o’ fun — December 31, 2008 @ 4:47 pm


RSS feed for comments on this post. TrackBack URI

Leave a comment

Blog at WordPress.com.