Hide hidden Zend_Form elements

The Zend_Form has support for hidden elements, Zend_Form_Element_Hidden. In default setup they are rendered in a <dt><dd> wrapper and this can raise some problems when applying styles to the form.

This blog article suggests a new idea to solve this problem. On the Internet many proposals are posted with all some drawbacks, which are solved in this idea.

By default a hidden element is rendered like this:

<dt><label for="element"></label></dt>
<dd><input type="hidden" name="element" id="element"></dd>

In this case the <dt> is empty but occupies some space which you can't remove by applying css rules. Therefore you'd like to add, for example, a class to the <dt> so you can apply a display:none.

Most methods use the Zend_Form::render() method in which they loop through all elements and grab the elements of the type Zend_Form_Element_Hidden. One example is the suggestion of Tudor Barbu. There are two drawbacks of this example:

  1. You cannot reach the elements inside subforms
  2. You loop through all form elements, which is a huge performance impact

What then?

The case is actually quite simple. Instead of using the Zend_Form::render(), you apply only the class to the hidden elements itself. The Zend_Form_Element_Hidden extends the Zend_Form_Element, so Zend_Form_Element_Hidden uses also the (general) Zend_Form_Element::loadDefaultDecorators() method. When we create our own My_Form_Element_Hidden class, overriding the loadDefaultDecorators() method we can add some additional features to the default decorators.

<?php
class My_Form_Element_Hidden extends Zend_Form_Element_Hidden
{
    /**
     * Load default decorators
     *
     * @return void
     */
    public function loadDefaultDecorators()
    {
        if ($this->loadDefaultDecoratorsIsDisabled()) {
            return;
        }

        $decorators = $this->getDecorators();
        if (empty($decorators)) {
            $this->addDecorator('ViewHelper')
                ->addDecorator('Errors')
                ->addDecorator('Description', array('tag' => 'p', 'class' => 'description'))
                ->addDecorator('HtmlTag', array('tag' => 'dd',
                                                'id'  => $this->getName() . '-element'))
                ->addDecorator('Label', array('tag' => 'dt',
                                              'class' => 'hidden'));
        }
    }
}

The only change made here is the added parameter 'class' => 'hidden' to the label. Now the <dt> gets a class hidden.

The last question is how we can initiate the My_Form_Element_Hidden instead of the Zend_Form_Element_Hidden. Zend_Form uses the elements by loading them like plugins. If we add our custom prefixPath to the form, Zend_Form will look into that path first and find our My_Form_Element_Hidden first. Other elements (like My_Form_Element_Text) are not found and the Zend_Form_Element_* classes will be loaded.

<?php
class My_Form extends Zend_Form
{
    public function __construct($options = null)
    {
        $this->addPrefixPath('My_Form_', 'My/Form/');
        parent::__construct($options);
    }
}

Now we're finished. We have a file library/My/Form.php to load the hidden element automatically from our namespace. We also have a library/My/Form/Element/Hidden.php to adjust the default decorators. We end up with this html and can apply a css rule to hide the <dt> line.

If you create your form, keep in mind you don't need to extend the Zend_Form but your own My_Form. Then all hidden elements are hidden automatically (if the correct css is loaded), because the html is now like this:

<dt class="hidden"><label for="element"></label></dt>
<dd><input type="hidden" name="element" id="element"></dd>