Zend Framework

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

Details

  • Type: Bug Bug
  • Status: Resolved Resolved
  • Priority: Minor Minor
  • Resolution: Cannot Reproduce
  • Affects Version/s: 1.0.2
  • Fix Version/s: 1.8.1
  • Component/s: Zend_Validate
  • Labels:
    None

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.

Issue Links

Activity

Hide
Darby Felton added a comment -

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.
Show
Darby Felton added a comment - 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.
Hide
Darby Felton added a comment -

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

Show
Darby Felton added a comment - Reducing priority to minor, since an easy workaround should be present.
Hide
Wil Sinclair added a comment -

Please evaluate and categorize/assign as necessary.

Show
Wil Sinclair added a comment - Please evaluate and categorize/assign as necessary.
Hide
Andries Seutens added a comment -

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"
}
Show
Andries Seutens added a comment - 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"
}
Hide
Andries Seutens added a comment -

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.

Show
Andries Seutens added a comment - 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.
Hide
Travis Pew added a comment -

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).

Show
Travis Pew added a comment - 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).
Hide
Thomas Weidner added a comment -

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.

Show
Thomas Weidner added a comment - 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.
Hide
Thomas Weidner added a comment -

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

Show
Thomas Weidner added a comment - Closing issue as setErrorMessage does exactly what was described as problem in this issue.

People

Vote (3)
Watch (7)

Dates

  • Created:
    Updated:
    Resolved: