Zend Form – Validation Messages in Config

Zend Form default validation messages are often useless especially if those error messages are not displayed close to the form element. That’s why, as part of learning experience, I have automated the overriding of validations messages using a configuration file.

Off-topic: By the way, I have just uploaded my small library collection for Zend Framework at github. It is a compilation of classes I created to speed up my development time and to unify things when using Zend Framework. And part of it is the basic extension to Zend_Form.

Note: You can actually set every pieces of your form in a config file but in my example, I’m only talking about error messages.

Overview

This article assumes that you are familiar about Zend_Form and Zend_Validate classes of Zend Framework. Assuming that you have a very basic form that looks like this:

<?php

class Default_Form_User extends Zend_Form
{
	public function init()
	{	
		// Precious username
		$username = new Zend_Form_Element_Text('username');
		$username->setRequired(true)
			->setLabel('Username')
			->addFilter('StringTrim')
			->addValidator('StringLength', true, array(
				'min' => 3,
				'max' => 16
			))
			->addValidator('Alnum', true)
			->addValidator('Callback', true, array(
				'callback' => array($this, 'usernameUnique')
			))
			->setDecorators(array(
				array('ViewHelper')
			));
			
		$this->addElement($username);
	}
}

As you can see, it is just a plain, bare bone form class that extends Zend_Form with only one field username. It has several validators such as validation if the username is not empty, it has to between 3-16 characters long, composed of letters of the alphabet and numbers and must be unique – which means the username is not yet registered by another user in the system.

It is pretty simple. However, when you validated the form, you will get weird messages like:

Value is required and can’t be empty

and the like instead of something like:

Username is not entered

or something similar. Translation does not work here because it is not about translating the message but changing the message into something customized to your own need.

Overriding Validation Messages

To override the messages of our validators, there are several ways and I’m giving the simplest one that will work out of the box. Override the messages as soon as the validator is added.

<?php

class Default_Form_User extends Zend_Form
{
	public function init()
	{	
		// Precious username
		$username = new Zend_Form_Element_Text('username');
		$username->setRequired(true)
			->setLabel('Username')
			->addFilter('StringTrim')
			->addValidator('StringLength', true, array(
				'min' => 3,
				'max' => 16,
				'messages' => array(
					Zend_Validate_StringLength::INVALID => 'Invalid username entered',
					Zend_Validate_StringLength::TOO_SHORT => 'Username must be at least %min% characters',
					Zend_Validate_StringLength::TOO_LONG => 'Username must not be more than %max% characters'
				)
			))
			->addValidator('Alnum', true, array(
				'messages' => array(
					Zend_Validate_Alnum::INVALID => 'Username must be a valid name',
					Zend_Validate_Alnum::NOT_ALNUM => 'Username must be composed of letters and numbers only',
					Zend_Validate_Alnum::STRING_EMPTY => 'Username is not entered'
				)
			))
			->addValidator('Callback', true, array(
				'callback' => array($this, 'usernameUnique'),
				'messages' => array(
					Zend_Validate_Callback::INVALID_CALLBACK => 'Unable to check for username uniqueness',
					Zend_Validate_Callback::INVALID_VALUE => 'Username already exists, choose a different username'
				)
			))
			->setDecorators(array(
				array('ViewHelper')
			));
			
		$this->addElement($username);
	}
}

This started to get messy isn’t it? You can actually use the validation message key value directly instead of constants. For example, we can use notAlnum instead of Zend_Validate_Alnum::NOT_ALNUM. We can also store those messages somewhere else and retrieve the messages and assign it to each validators at once.

->addValidator('Alnum', true, array(
	'messages' => $arrayOfMessagesForAlnum
))

However, it is still messy. We can remove those codes altogether.

Overriding Validation Messages – After init()

We can actually override every piece of most of the Zend Framework components after they have been initialized and Zend_Form and Zend_Validate is also one of them. Assuming that we didn’t assign messages on Alnum validator. Here is how we override them after the form is initialized.

public function initValidationMessages()
{
	$this->getElement('username')->getValidator('Alnum')->setMessages($arrayOfMessagesForAlnum);
}

With that in mind, we can set all those validation messages in a config file and load them automatically. Here is the proposed directory structure for our example.

project
    + application/
	    + configs/
		    + user/
			    form-messages.ini
		+ forms/
		    User.php

Our configuration file is at: application/configs/user/form-messages.ini and here is the content:

[production]

username.NotEmpty.notEmptyInvalid = "Username is not entered"
username.NotEmpty.isEmpty = "Username is not entered"
username.StringLength.stringLengthInvalid = "Username must be a valid name"
username.StringLength.stringLengthTooShort = "Username must be at least %min% characters"
username.StringLength.stringLengthTooLong = "Username must be not more than %max% characters"
username.Alnum.alnumInvalid = "Username must be a valid name"
username.Alnum.notAlnum = "Username must be composed of letters and numbers only"
username.Alnum.alnumStringEmpty = "Username is not entered"
username.Callback.callbackInvalid = "Unable to check for username uniqueness"
username.Callback.callbackValue = "Username already exists, choose a different username"

[staging : production]

[testing : production]

[development : production]

Note that we use the actual error message key here – because we cannot call Zend Framework constants in a .ini file. We have to update our form in order to load the messages from the config file.

public function loadMessageConfig()
{
	$messages = new Zend_Config_Ini(
		APPLICATION_PATH.'/configs/user/form-messages.ini', 
		APPLICATION_ENV
	);
	
	return $messages->toArray();
}

We added a method that will load the messages into an array. Next we need to update the overriding of error messages that will now use the information from our config file.

public function initValidationMessages()
{
	$messages = $this->loadMessageConfig();
	
	if ( ! empty($messages))
	{
		foreach ($messages as $field => $fieldMessages)
		{
			foreach ($fieldMessages as $validator => $validatorMessages)
			{
				$element = $this->getElement($field);
				$validate = null;
				
				if ($element)
				{
					$validate = $element->getValidator($validator);
				}
				
				if ($validate)
				{
					$validate->setMessages($validatorMessages);
				}
			}
		}
	}
	
	return $this;
}

The updated method makes sure that non-existing fields or validator will not throw errors.

So our final form will look like this:

<?php

class Default_Form_User extends Zend_Form
{
	public function init()
	{	
		// Precious username
		$username = new Zend_Form_Element_Text('username');
		$username->setRequired(true)
			->setLabel('Username')
			->addFilter('StringTrim')
			->addValidator('NotEmpty')
			->addValidator('StringLength', true, array(
				'min' => 3,
				'max' => 16
			))
			->addValidator('Alnum', true)
			->addValidator('Callback', true, array(
				'callback' => array($this, 'usernameUnique')
			))
			->setDecorators(array(
				array('ViewHelper')
			));
			
		$this->addElement($username);
		
		// Load the messages
		$this->initValidationMessages();
	}
	
	public function loadMessageConfig()
	{
		$messages = new Zend_Config_Ini(
			APPLICATION_PATH.'/configs/user/form-messages.ini', 
			APPLICATION_ENV
		);
		
		return $messages->toArray();
	}
	
	public function initValidationMessages()
	{
		$messages = $this->loadMessageConfig();
		
		if ( ! empty($messages))
		{
			foreach ($messages as $field => $fieldMessages)
			{
				foreach ($fieldMessages as $validator => $validatorMessages)
				{
					$element = $this->getElement($field);
					$validate = null;
					
					if ($element)
					{
						$validate = $element->getValidator($validator);
					}
					
					if ($validate)
					{
						$validate->setMessages($validatorMessages);
					}
				}
			}
		}
		
		return $this;
	}
}

Note that we added NotEmpty validator which is just equivalent to setRequired(true). We did that because it is hard to override the required field error message without that – or tell me if you make it work.

The form class can still be improved. Assuming that you have bunch of forms, you can create a base form and automate things during __conscturct() or int() methods of the base form. I have done that in my template form.

This entry was posted in Zend Framework and tagged , , , , , , , . Bookmark the permalink.

5 Responses to Zend Form – Validation Messages in Config

  1. Pingback: Tweets that mention Zend Form – Validation Messages in Config | Lysender's Daily Log Book -- Topsy.com

  2. Sanjay says:

    Hi,
    I am new in Zend. I read your article and it quite helpful to me. Just a one thing I don’t understand is where should I put usernameUnique function. If possible please explain the flow of callback validator.

    Thanks in advance.

  3. lysender says:

    Hi Sanjay,

    The usernameUnique in this example is a custom method in the Form that checks for the username and returns true/false. I haven’t included it on the post but will update it soon as I have time.

  4. tarun says:

    Hi lysender ,
    very well written article and i really enjoyed it.
    If i have dozen of form in my application and in each form i have say 3 fields
    so that means i have to create 36 form.ini files,thats look overkill.
    would not better if we override errormessage properties of validation classes?
    If we could use someplaceholder like %value in your form.ini that would be better i guess

  5. lysender says:

    Hi tarun,

    You don’t have to create 36 ini files. Just put all messages on a single ini file and group them by form and then group by fields.

Leave a Reply

Your email address will not be published. Required fields are marked *