Issues

ZF-9002: Warning on form validation

Description

This is essentially a re-open of ZF-4030. I could not find a way to change the status of that issue.

Zend_Form_Decorator_Errors produces PHP warnings and erroneous output if $element->getMessages() produces a multi-dimensional array of errors instead of a single-dimensional array of messages. This will happen if the Errors decorator is attached to a form and the form has no form-level errors (e.g. messages added via addError()), but does contain elements with errors.

Suggested fix is to remove errors that are not strings:

Zend_Form_Decorator_Errors (line 53):

    // remove errors that are not strings.
    $errors = $element->getMessages();
    foreach ($errors as $key => $error) {
        if (!is_string($error)) {
            unset($errors[$key]);
        }
    } 

The crux of the problem is actually the behaviour of Zend_Form::getMessages(), but that would be difficult to resolve and may break BC.

Comments

Why are you using the Errors decorator on a form object instead of the FormErrors decorator (which was designed specifically for handling the multi-dimensional arrays returned by Zend_Form::getMessages())?

Right now, it feels like you're using the wrong tool for the job. However, if you have a good reason for this and can detail the use case sufficiently, I'll investigate further.

Fair question. The FormErrors decorator has different behavior. It displays all of the implicit errors on a form (e.g. errors on the elements of the form). It does not display errors that are directly attached to the form (e.g. errors set explicitly via $form->addError()).

In this case, I want to display element errors on the elements, but form-level errors at the top of the form. The proposed fix allows the errors decorator to be used on both elements and forms. In my case it produces the desired behavior. It's also defensive which is probably good.

...It is probably worth noting that the getMessages() method of the Zend_Form object contributes to the problem because it has a bit of a split personality. In the case where errors have been explicitly set on the form it will return a single-dimensional array and will not include any of the implicit/element errors. If, however, the form does not have explicit errors, but does have elements with errors, it will return a multi-dimensional array.

In the first case, the standard Errors decorator works fine on forms. In the second case it produces warnings and erroneous output.

Hope that helps!

Stewart -- thanks for clarifying. I'll look into the behavior this coming month.

In the meantime, can you provide an example form that displays the issue? That will make writing up a test case easier. Thanks!

Sure, np...

This works fine:

    $form = new Zend_Form;
    $form->setView(new Zend_View);
    $form->addDecorator('Errors');
    $form->addElement('text','text');
    $form->addError('form-badness');
    echo $form;

This breaks:

    $form = new Zend_Form;
    $form->setView(new Zend_View);
    $form->addDecorator('Errors');
    $form->addElement('text','text');
    $form->getElement('text')->addError('element-badness');
    echo $form;

FormErrors doesn't solve the problem because it won't show 'form-badness', only 'element-badness':

    $form = new Zend_Form;
    $form->setView(new Zend_View);
    $form->addDecorator('FormErrors');
    $form->addElement('text','text');
    $form->addError('form-badness');
    $form->getElement('text')->addError('element-badness');
    echo $form;

This is fixed in trunk r22272 and merged into 1.10 release branch, in a way that FormErrors now render custom form errors.

Hi Christian, does the Errors decorator still produce warnings? Having FormErrors display both element-level errors and form level errors is not necessarily desirable. Consider the use case where you want form-level errors displayed at the top of the form, and element-level errors display next to the element.

Hi Stewart, uhh yes the Errors Decorator still produces a warning about htmlspecialchar getting wrong parameter type - look into it right after writing. For the FormErrors i introduced two more options showCustomFormErrors = (bool) & onlyCustomFormErrors (bool) as you can see in r22272, is it that what you meant?

Thanks for taking me back to that one :) don't know what i was doing, now there is a fix for the previous commit r22316 that does what i expected it to do.

So finally i think you can go with


$form->addDecorator('FormErrors', array('onlyCustomFormErrors' => true))
     ->addError('form-badness')
     ->addElement('text','text')
     ->text->addError('element-badness');

No more need to set Errors Decorator on the form :D

Now we may discuss if the default setting for onlyCustomFormErrors should be true instead of false.

Hi Christian, thanks for the follow-up. Those options are helpful. Yes, given that elements have the 'Errors' decorator by default, I think that onlyCustomFormErrors should be set to true initially.

Although the Errors decorator is no longer needed at the form-level, it might still be a good idea to apply the patch from the original description above. It's defensive and if someone attaches the Errors decorator to a form it will work. It's not obvious that you need one decorator for elements and another for the form.