One thing I really hate about forms in Zend Framework is the fact that the hidden fields are displayed as blank lines – sounds stupid but this is what really happens – and if you have more than one – let’s say 4 or 5 – their presence can become really annoying and ruin the layout. This happens because although the hidden fields aren’t shown by the browser, their wrapping tags are. ZF wraps all the form elements in additional markup with the help of decorators. The default XHTML for a hidden field in a Zend_Form looks something like this:

<dt>&nbsp;</dt>
<dd><input type="hidden" id="myElement" name="myElement" /></dd>

The definition tags have display:block, so they insert a lot of empty spaces in the layout. Which in the end will make the designer very unhappy. The solution is to remove all the definition tags or to hide them. Since this is presentational matter, it should fall to a view helper. The first idea was to remove the tags completely for hidden fields. Like such:

class Motanelu_View_Helper_FormFixer {
    public function formFixer( Zend_Form $_form ) {
        foreach( $_form->getElements() as $element ) {
            if( $element instanceof Zend_Form_Element_Hidden ) {
                $element->removeDecorator( 'HtmlTag' );
                $element->removeDecorator( 'Label' );
            }
        }
        return $_form;
    }
}

…and in the view (assuming that the helper is the the right path and will be picked out automatically by Zend Framework)…

echo $this->formFixer( $this->form );

Should do the trick. And it does. A lame trick :) If there are more elements in the form, the resulting markup will look something like this:

<form enctype="application/x-www-form-urlencoded" action="" method="post">
    <dl class="zend_form">
        <dt><label for="name" class="optional">Enter your name</label></dt>
        <dd><input type="text" name="name" id="name" value="" /></dd>
        <input type="hidden" name="id" value="" id="id" />
        <dt>&nbsp;</dt>
        <dd><input type="submit" name="submit" id="submit" value="Go go go!" /></dd>
    </dl>
</form>

Yep, that’s right! Invalid code. Not good. Another approach would be to hide the dt and dd tags that wrap around hidden fields with CSS. First attempt:

class Motanelu_View_Helper_FormFixer {
    public function formFixer( Zend_Form $_form ) {
        foreach( $_form->getElements() as $element ) {
            if( $element instanceof Zend_Form_Element_Hidden ) {
                foreach( $element->getDecorators() as $decorator ) {
                    $decorator->setOption( 'class', 'hidden' );
                }
            }
        }
        return $_form;
    }
}

But this doesn’t work as expected. The code above will produce:

<dt>&nbsp;</dt>
<dd class="hidden"><input type="hidden" name="id" value="" id="id" /></dd>

The dt doesn’t have the hidden class added to it. After some debugging, I’ve found why. It’s not a bug, it’s an…well…undocumented feature of Zend Framework.

// Zend_Form_Decorator_Label - line 306
if (!empty($label)) {
    $options['class'] = $class;
    $label = $view->formLabel($element->getFullyQualifiedName(), trim($label), $options);
} else {
    $label = '&nbsp;';
}

I’m very curious who is the rocket scientist that came up with this idea and more important why. Why shouldn’t I be allowed to change the class of an empty label? Well, never mind…But since it’s a very bad practice to change a framework’s source code, I’ve looked for a workaround. And found one: if the element doesn’t have a label, I’ll add a blank one. Like such:

class Motanelu_View_Helper_FormFixer {
    public function formFixer( Zend_Form $_form ) {
        foreach( $_form->getElements() as $element ) {
            if( $element instanceof Zend_Form_Element_Hidden ) {
                $label = $element->getLabel();
                if( empty( $label ) ) {
                    $element->setLabel( '&nbsp;' );
                }
                foreach( $element->getDecorators() as $decorator ) {
                    $decorator->setOption( 'class', 'hidden' );
                }
            }
        }
        return $_form;
    }
}

After adding the following code to my CSS file

.hidden {
    display: none;
}

…I can finally say that it works, that all the hidden fields stay hidden and that the resulting markup will validate even against the draconian XHTML Strict specification of the W3C.