ZF-2224: Behavior of Zend_Validate_Abstract::setMessage() and Zend_Validate_EmailAddress doesn't work as expected

Description

In the following scenario, i want to check an email address for validity and show the user only a single message that the email address which was entered is invalid. (this validator was used in combination with Zend_Filter_Input where the 'messages' => '...' key in the array is ignored in that case)

        $emailToCheck = 'foo@bar.com)'; // note the ')'

        $emailValidator = new Zend_Validate_EmailAddress();
        $emailValidator->setMessage('Please enter a valid email address.');

        $valid = $emailValidator->isValid($emailToCheck);
        var_dump($valid);
        if (!$valid) {
            var_dump($emailValidator->getMessages());
        }

I didn't set any key for the messages so I expected that all error messages are set to my specific error message but when I looked at the implementation of Zend_Validate_Abstract::setMessage(), only the first message key gets that message:

public function setMessage($messageString, $messageKey = null)
{
        if ($messageKey === null) {
            $keys = array_keys($this->_messageTemplates);
            $messageKey = current($keys);
        }
        [...]
}

So I would expect that I can set all messages to my defined error message but neither setMessage() nor setMessages() has this implemented.

The next problem is located in the Zend_Validate_EmailAddress validator, where the hostname validator passes all messages and errors to the to Zend_Validate_EmailAddress validator:

        $hostnameResult = $this->hostnameValidator->isValid($this->_hostname);
        if (!$hostnameResult) {
            $this->_error(self::INVALID_HOSTNAME);

            // Get messages and errors from hostnameValidator
            foreach ($this->hostnameValidator->getMessages() as $message) {
                $this->_messages[] = $message;
            }
            foreach ($this->hostnameValidator->getErrors() as $error) {
                $this->_errors[] = $error;
            }
        }

There is no possibility to break the chain at any place to only get one single user defined error message when the hostname was invalid.

Comments

Each validation class can emit multiple messages, each of which corresponding to a cause for validation failure. A validation failure may indeed be caused by more than one reason, and this is why validation classes can emit multiple messages. There is no method in the public API to change all of the validation failure messages to a single message because this would obfuscate the reasons for validation failure. These reasons are not necessarily intended for display to users, however, and this is where the confusion seems to be.

Of course, you may only want to show a single, consolidated validation failure message to your end users, and this is a common and perfectly valid use case.

It should be simple to implement in at least one of the following ways:

  • If you use the validation class with composition, you can detect whether a validation failure has occurred and display whatever message you want to users.
  • Extend the validation class and perform the message consolidation there.
  • Try Zend_Form for consolidating validation failure messages for display to users.

Reducing priority to minor, since an easy workaround should be present.

Please evaluate and categorize/assign as necessary.

The validation failure messages are not really meant for user consumption. However, I undestand that a lot of people are using them this way.

I suggest that we create 2 new methods:



$validator = new Zend_Validate_StringLength(2);

$validator->setUserMessage(
    'Your firstname should be at least 2 characters long',
    Zend_Validate_StringLength::TOO_SHORT);

if (!$validator->isValid('A')) {
    $messages = $validator->getUserMessages();
    echo current($messages);

    // echoes "Your firstname should be at least 2 characters long"
}

I would also like to note that it would be nice if created a special constant that overrides all error messages. This is particulary usefull for the email validator:


$validator->setUserMessage(
    'Your email address it not valid'
    Zend_Validate::OVERRIDE_ALL);

this would override all error messages in the stack.

It seems strange to state that the validation failure messages are "not really meant for user consumption" given that Zend_Form by default renders these messages in the form when validation fails and that Zend_Validate automatically will use Zend_Translate if set in the registry to translate its messages for the user.

In particular, the inability to set a single error message breaks the ability to quickly and easily add a Text form element that uses the EmailAddress validator and have a single message such as "Invalid e-mail address" be provided by Zend_Translate. For example, if one sets all of the error messages to "Invalid e-mail address", "Invalid e-mail address" will appear 3 times when a user enters the email address "test@!".

The "easy" workaround for the developer is of course to extend Zend_Validate_EmailAddress (and every other validation class that can output multiple methods) to consolidate error messages after Zend_Validate_EmailAddress' isValid() function is called. However, it seems to be a very common use case to want to display a single simple error message to the user (and there have been a number of questions about this exact problem).

When you want to display a single error from an failed validation use setError() on the form element. This has nothing to do with the bahaviour or the messages returned from the element itself and is only related to the form which is being displayed to the user.

Another way is to define the messages option on the form element which does the same.

Closing issue as setErrorMessage does exactly what was described as problem in this issue.