views:

56

answers:

1

I've set a validator for the email so it can't be empty.

This is the markup of the usual form that zend_form generates:

<dt id="email-label"><label class="required" for="email">Email</label></dt>
<dd id="email-element">
   <input type="text" value="" id="email" name="email">
</dd>

When validation fails, zend_form adds a new ul class="errors" inside the dd

<dt id="email-label"><label class="required" for="email">Email</label></dt>
<dd id="email-element">
   <input type="text" value="" id="email" name="email">
   <ul class="errors">
      <li>Value is required and can't be empty</li>
   </ul>
</dd>

How can I change this default behavior slightly so that the entire dt dd gets wrapped in a single p or something that I can add an error class to? My guess is that I need to specify to zend_form how to behave when an element has errors.

+3  A: 

You can create your own decorator to do that, something simple like this::

class My_Decorator_ElementWrapper extends Zend_Form_Decorator_Abstract
{
    public function render($content)
    {
        $class = 'form-element';
        $errors = $this->getElement()->getMessages();
        if (!empty($errors))
            $errors .= ' has-errors';
        return '<div class="'.$class.'">' . $content . '</div>';
    }
}

Now you can just register this decorator for the element:

$element->addPrefixPath('My_Decorator', 'My/Decorator/', 'decorator');
$element->addDecorator('ElementWrapper');

You can also register the prefix path for all elements at the same time by using $form->addElementPrefixPath() instead.

If you want to add this decorator (and the prefix path) automatically for all elements, I suggest you extend each element the Zend's corresponding one (eg. make My_Form_Element_Text that extends Zend_Form_Element_Text), and then add the prefix path in the init function, and override loadDefaultDecorators() method to add the ElementWrapper at the end of the decorator chain. For example, this is how loadDefaultDecorators() looks for Zend_Form_Element_Text:

public function loadDefaultDecorators()
{
    if ($this->loadDefaultDecoratorsIsDisabled()) {
        return $this;
    }

    $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'));
    }
    return $this;
}

You'd just add a ->addDecorator('ElementWrapper') at the end of the chain. So to show a concrete example of My_Form_Element_Text:

class My_Form_Element_Text extends Zend_Form_Element_Text
{
    public function init()
    {
        $this->addPrefixPath('My_Decorator', 'My/Decorator/', 'decorator');
    }

    public function loadDefaultDecorators()
    {
        if ($this->loadDefaultDecoratorsIsDisabled()) {
            return $this;
        }

        $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'))
                ->addDecorator('ElementWrapper');
        }
        return $this;
    }
}
reko_t