Globally overriding validation messages for ZF2 forms

One thing that I always do when creating a Zend Framework 2 form is override the validation messages for a number of validators – EmailAddress in particular.

I recently decided that I should probably sort this one out once and be done with it. Turns out that it's quite easy assuming that you use the FormElementManger to instantiate your forms.

All that I need to do is create my own validator classes that extend the Zend Framework ones and just set new message templates. This is what EmailAddress looks like:

namespace RKA\Validator;

use Zend\Validator\EmailAddress as BaseEmailAddress;

class EmailAddress extends BaseEmailAddress
{
    protected $messageTemplates = array(
        self::INVALID            => "Invalid type given. String expected",
        self::INVALID_FORMAT     => "Invalid email address",
        self::INVALID_HOSTNAME   => "Invalid email address",
        self::INVALID_MX_RECORD  => "Invalid email address",
        self::INVALID_SEGMENT    => "Invalid email address",
        self::DOT_ATOM           => "Invalid email address",
        self::QUOTED_STRING      => "Invalid email address",
        self::INVALID_LOCAL_PART => "Invalid email address",
        self::LENGTH_EXCEEDED    => "Email address is too long",
    );
}

To ensure that the ValidatorPluginManager uses the new validator in place of the default one, we override in module.config.php:

    'validators' => [
        'invokables' => [
            'email-address' => 'RKA\Validator\EmailAddress',
            'string-length' => 'RKA\Validator\StringLength',
            // etc.
        ],
    ],

Now, whenever you create a form with input filter that uses FormAbstractServiceFactory or via a config array with the Form\Factory as set up in my last post, then the new validator is picked up (assuming you use the short name).

If you create your forms via a class, then you should use the FormElementManager like this.

Firstly, create your form:

namespace RKA\Form;

use Zend\Form\Form;
use Zend\InputFilter\InputFilterProviderInterface;

class ExampleForm extends Form implements InputFilterProviderInterface
{
    public function init()
    {
        $this->add([
            'name' => 'email',
            'type' => 'text',
            'options' => [
                'label' => 'Email address',
            ],
            'attributes' => [
                'class'    => 'form-control',
                'required' => 'required',
            ],
        ]);

        $this->add([
            'name' => 'submit',
            'type' => 'button',
            'options' => [
                'label' => 'Go!',
            ],
            'attributes' => [
                'class' => 'btn btn-default',
            ],
        ]);
    }

    public function getInputFilterSpecification()
    {
        return [
            'email' => [
                'required' => true,
                'filters'  => [
                    ['name' => 'StringTrim'],
                    ['name' => 'StripTags'],
                ],
                'validators' => [
                    ['name' => 'EmailAddress'],
                ],
            ],
        ];
    }
}

Note that in getInputFilterSpecification we use the service manager key names for each validator name. The ones that are shipped with ZF2 are listed in the ValidatorPluginManager.

Now register it with the FormElementManager in module.config.php:

    'form_elements' => [
        'invokables' => [
            'RKA\ExampleForm'  => 'RKA\Form\ExampleForm',
        ],
    ],

You can then instantiate it in your controller via the main service locator:

    // in my controller
    public function getExampleForm()
    {
        $formManager = $this->getServiceLocator()->get('FormElementManager');
        return $formManager->get('RKA\ExampleForm');
    }

In addition to forms, you can also register your own fieldsets and elements under the 'form_elements' key and then use them within your forms.

2 thoughts on “Globally overriding validation messages for ZF2 forms

  1. I'm not sure whether it makes sense to override validator just because of the need for some custom error messages…

    In my opinion, better approach would be to manage those messages through the Translator, as all of those message keys (emailAddressInvalid, emailAddressInvalidFormat, etc.) are being used directly during translation (translateMessage() method of the AbstractValidator).

    Another solution would be to specify overrides for messages in the input filter specification itself:

    array(
      'name' => 'email',
      'required' => true,
      'validators' => array(
        array(
          'name'=>'EmailAddress', 
          'options'=>array(
            'messages'=>array(
              'emailAddressInvalid'         => "Invalid type given. String expected",
              'emailAddressInvalidFormat'   => "Invalid email address",
              'emailAddressInvalidHostname' => "Invalid email address",
            )
          )
        )
      ),
    ),

    But ok, if the idea was to generally override messages of some validator, for all the use cases, then your solution is a proper way for achieving that.

    1. Nikola,

      Yes, I suspect that using the translator would be another good way to achieve this. I'm trying to avoid having to set the error message in the input filter as it has to be done every single time, which is a little tedious!

      Rob…

Leave a Reply

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>