Zend_Validate_Callback with no context

Photo by Kelly Sikkema on Unsplash.

This class is super simple, so we'll begin with the code.

class My_Validate_Callback_NoContext extends Zend_Validate_Callback
{
    public function isValid($value)
    {
        return parent::isValid($value);
    }
}

How does it work?

During Zend_Form->isValid(), every validator on the form is passed an extra parameter called the "context". The context contains all of the data submitted with the form. This lets you validate one form field based on the value of another, like a Password Confirm field that must match a Password field.

One of the Zend Framework standard validators is Zend_Validate_Callback, which lets you validate the field using any function. For example, a Username field could validate with the following function:

public function isUsernameAvailable($username, $excludeId = FALSE)

The $excludeId parameter is supplied when the user has an existing account, and omitted for new user registrations. For example:

$userMapper = new Application_Model_Mapper_Users();
$username = new Zend_Form_Element_Text('username');
$username->addValidator('Callback', FALSE, array(
    'callback' => array($userMapper, 'isUsernameAvailable'),
    'options' => FALSE));
$userMapper = new Application_Model_Mapper_Users();
$username = new Zend_Form_Element_Text('username');
$username->addValidator('Callback', FALSE, array(
    'callback' => array($userMapper, 'isUsernameAvailable'),
    'options' => $this->_applicantId));

(In this case, you might also use Zend_Validate_Db_NoRecordExists, but I prefer not to leak so much model information into the form validation.)

What's the problem?

As it stands, the Register.php form above will trigger a Zend_Validate_Callback::INVALID_CALLBACK error:

An exception has been raised within the callback

That's because the form context will be appended as a parameter to the callback and interpreted as the $excludeId, fouling the database lookup.

Cue the custom validator. By overriding the Zend_Validate_Callback->isValid() function, we'll drop the form context before invoking the callback function. The registration form would be updated as follows:

$userMapper = new Application_Model_Mapper_Users();
$username = new Zend_Form_Element_Text('username');
$username->addPrefixPath('My_Validate', 'My/Validate/', 'validate')
    ->addValidator('Callback_NoContext', FALSE, array(
        'callback' => array($userMapper, 'isUsernameAvailable'),
        'options' => FALSE));

Now you can setup callback validators without worrying about the default parameters of the callback function.

Drew

Drew

Hi! I'm Drew, the Wimpy Programmer. I'm a software developer and formerly a Windows server administrator. I use this blog to share my mistakes and ideas.