Setting Custom Error Messages for Zend_Form_Element

Tweet

In this article I’ll show the solution to a common problem which arises when a developer has to create a non-English website or application that is based on the Zend Framework. How do we mark a field of a Zend_Form which is a Zend_Form_Element as invalid, showing one or more custom error messages? The problem is mainly caused by the native counter-intuitive methods of the Zend_Form_Element class which I’ll explain in more details. (Note the problem and the solution discussed here is valid for Zend Framework version 1.12 and below.)

Developing an Example

Let’s say that you have a form with different fields and take into account just one of them, for example an text input field used to let a user enter his name. The validators that you can use are different, but for the sake of example we’ll limit the length of the name and it’ll only allow alphabetic characters and spaces. Speaking the Zend Framework language, we’ll use the Zend_Validate_StringLength and the Zend_Validate_Alpha classes respectively.

As you may already know, Zend Framework is owned by the American company Zend and so all of the messages it shows are in English. The default behavior of the framework is to display one or more error messages for every validator broken by the user input. So, for those who are building a non-English website, there are two options to have messages readable by all users: translate every error message of the framework, or explain in one or more messages that the input is invalid and show suggestions to insert an accepted value. The first option is over-complicated, especially for small and medium-sized projects, so I’ll show how to use the second option.

To keep it easy, we will set a single custom message: “The input is invalid. The value must have only alphabetic characters and spaces and its length must be between 3 and 50 characters.”

The Code

I’ll show the code that we’ll use during the article. First, this is the form that contains the input field for the name and the validators needed to check the data.

<?php
class Application_Form_User extends Zend_Form
{
    public function init() {
        // create the field
        $element = new Zend_Form_Element_Text("name");
        $element->setLabel("Name");

        // set the validators
        $element->setValidators(array(
            new Zend_Validate_Alpha(true),
            new Zend_Validate_StringLength(
                array("min" => 3, "max" => 50))
        ));
        $element->setRequired();

        // add the element to the form
        $this->addElement($element);

        // add a submit button
        $element = new Zend_Form_Element_Submit("submit");
        $element->setLabel("Submit");
        $this->addElement($element);
    }
}

In the controller we’ll check if the field is valid and act accordingly. Usually you won’t use the IndexController and probably you have this validation in a specific controller. Anyway, to simplify the example, I’ll use it.

<?php
class IndexController extends Zend_Controller_Action
{
    public function init() {
    }

    public function indexAction() {
        $form = new Application_Form_User();

        if ($this->getRequest()->isPost() &&
            $form->isValid($this->getRequest()->getPost())) {
            $this->view->message = "Valid input";
        }
        else {
            $this->view->form = $form;
        }
    }
}

The view used is quite simple; it only shows the message and the form.

<?php
if (isset($this->message)) {
    echo $this->message;
}
if (isset($this->form)) {
    echo $this->form;
}

The source code above, without any CSS rule, will render as such:

Moreover, if you insert the invalid value “88” in the Name field you’ll see the messages:

Analyzing the Framework’s Methods

A good question to ask is if there are already methods available to face this kind of situation. The answer is almost. I mean there are methods, but they don’t always work as you expect (at least as I expect). The methods to manage the error messages are:

  • setErrors(array $messages)
  • setErrorMessages(array $messages)

The method setErrors() has only one parameter which is an array and uses its elements as messages to show in case of invalid input. An example of its use is:

<?php
// set the custom message in the case of an error
$element->setErrors(array("The input is invalid. The value must have only alphabetic characters and spaces and its length must be between 3 and 50 characters."));

This method, as well as displaying the given string(s), also marks the field as invalid.

It can be used at two times in the application logic, but in both it has a behavior which is not useful for our goal. The first is during the creation of the form element (init() method). In this case the message is shown when the form has been loaded and before the user has inserted any data. Quite unpleasant. In this case the relevant part of code changes like this:

<?php
// set the validators
$element->setValidators(array(
    new Zend_Validate_Alpha(true),
    new Zend_Validate_StringLength(
        array("min" => 3, "max" => 50))
));
$element->setRequired();

// set the custom message in the case of an error
$element->setErrors(array("The input is invalid. The value must have only alphabetic characters and spaces and its length must be between 3 and 50 characters."));

The second time occurs after the information has been sent during the usual data validation within the controller. What happens is that the custom message is appended to the default ones when an error occurs. In this case, the indexAction() of the IndexController changes in this way:

<?php
public function indexAction() {
    $form = new Application_Form_User();
    if ($this->getRequest()->isPost()) {
        // If the input of the user is valid, set the success
        // message. Otherwise, set the custom errors and show
        // the form again.
        if ($form->isValid($this->getRequest()->getPost())) {
            $this->view->message = "Valid input";
        }
        else {
            $form->getElement("name")->setErrors(array("The input is invalid. The value must have only alphabetic characters and spaces and its length must be between 3 and 50 characters."));
            $this->view->form = $form;
        }
    }
    else {
        $this->view->form = $form;
    }
}

Just like setErrors(), the setErrorMessages() method take as a parameter an array of strings which will be shown to the user in case of invalid input. An example of its use is:

<?php
$element->setErrorMessages(array("The input is invalid. The value must have only alphabetic characters and spaces and its length must be between 3 and 50 characters."));

This line of code still doesn’t solve the problem beucase it will either show the same error message for every not-satisfied condition or it will have no effect. If the line shown is used in the init() method, in the same way shown for the setErrors(), in case of error, the custom message will be shown as many times as the number of conditions violated by the user input. If the line is inserted during the usual data validation within the controller, in the same way explained before, there will be no effect. This means that the custom message won’t be displayed and the framework will show only the default messages.

Uncovering the Solution

Now the point is to understand when and how to insert the custom message so that the user can have a comparison and a suggestion about what values are accepted. Definitely, the when lies in the creation of the field inside the form (init() method) and the how is due to the union of the setErrorMessages() method and the use of a property of the validators of Zend Framework called breakChainOnFailure(). The latter allows us to stop the validation process at the first failed condition. If we set up five validators but the first of them fails, the other four won’t be used.

To employ this property making use of the smallest possible number of code lines, I’ll change a little bit the code I have illustrated at the beginning. I’ll add to the init() method the code line which uses the setErrorMessages() method, and I’ll take advantage of one of the possible input configurations accepted by setValidators() which expects an array of arrays. The array contained in the main one can have a maximum of three parameters, which are:

  1. A string (mandatory) to specify the validator to user.
  2. A boolean (optional, by default its value is false) to specify if the framework has to break the validation at the first failure or not. Thus, this parameter sets the value of the property breakChainOnFailure which is what will help us in achieve our goal.
  3. An array of options (optional, by default an empty array) different for every choosen validator

Using the second parameter passed as true for each of the validators that you want to use is essential.

According to what we’ve seen until now, the resulting code is:

<?php
class Application_Form_User extends Zend_Form
{
    public function init() {
        // create the field
        $element = new Zend_Form_Element_Text("name");
        $element->setLabel("Name");

        // set the validators
        $element->setValidators(array(
            array("Alpha", true, array("allowWhiteSpace" => true)),
            array("StringLength", true, array(
                "min" => 3, "max" => 50))
        ));
        $element->setRequired();

        // set the custom message in the case of an error
        $element->setErrorMessages(array("The input is invalid. The value must have only alphabetic characters and spaces and its length must be between 3 and 50 characters."));

        // add the element to the form
        $this->addElement($element);

        // add a submit button
        $element = new Zend_Form_Element_Submit("submit");
        $element->setLabel("Submit");
        $this->addElement($element);
    }
}

Conclusion

Using the code above, the form will show only the custom message when there’s invalid input… exactly what we wanted! In case you need to use more messages, for example suggestions in several stages, just add more strings to the array used for the setErrorMessages() method.

Image via Fotolia

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Uncle Fred

    Love the writeup. Please do Zend Framework 2 articles hence they are in short supply.

    • Okeowo Aderemi

      i agree ZF2 documentations are in rare supply, but here are a links http://samsonasik.wordpress.com/ he writes about Zend 2 alot but would love to see from PHP Masters i used the framework and its really marvelous,at least its quite flexible. :)

    • http://www.audero.it/ Aurelio De Rosa

      I’m glad you liked it. In the next articles I’ll touch ZF2.

  • Guest
    • http://www.audero.it/ Aurelio De Rosa

      How exactly this reference solve the problem I pointed out? They simply don’t if you use them in the way you expected they work. If you don’t use the breakChainOnFailure(), those methods are completely useless to solve the issue.