How can I pass an empty value through Zend framework 2 ValidatorChain to my custom validator?
It was possible on ZF1 by allowEmpty(false)
On ZF2 with empty element value :
If allowEmpty = false
, NotEmptyValidator is added to the top of ValidatorChain with breakOnFailure = true
, @see Zend/InputFilter/Input#injectNotEmptyValidator
.
If allowEmpty = true
, Element is considered as Valid, @see Zend/InputFilter/BaseInputFilter#isValid
if ($input->allowEmpty()) {
$this->validInputs[$name] = $input;
continue;
}
Following works for ZF2 version 2.1.1:
The problem (if I got it correctly) is that in following example, for empty values of 'fieldName'
, no validation is triggered. This can be quite annoying, though in
$input = new \Zend\InputFilter\Input('fieldName');
$input
->setAllowEmpty(true)
->setRequired(false)
->getValidatorChain()
->attach(new \Zend\Validator\Callback(function ($value) {
echo 'called validator!';
return true; // valid
}));
$inputFilter = new \Zend\InputFilter\InputFilter();
$inputFilter->add($input);
$inputFilter->setData(array('fieldName' => 'value'));
var_dump($inputFilter->isValid()); // true, echoes 'called validator!'
$inputFilter->setData(array('fieldName' => ''));
var_dump($inputFilter->isValid()); // true, no output
$inputFilter->setData(array());
var_dump($inputFilter->isValid()); // true, no output
This is quite annoying when you have particular cases, like checking an URL assigned to a page in your CMS and avoiding collisions (empty URL is still an URL!).
There's a way of handling this for empty strings, which is to basically attach the NotEmpty
validator on your own, and avoiding calls to setRequired
and setAllowEmpty
. This will basically tell Zend\InputFilter\Input#injectNotEmptyValidator
not to utomatically attach a NotEmpty
validator on its own:
$input = new \Zend\InputFilter\Input('fieldName');
$input
->getValidatorChain()
->attach(new \Zend\Validator\NotEmpty(\Zend\Validator\NotEmpty::NULL))
->attach(new \Zend\Validator\Callback(function ($value) {
echo 'called validator!';
return true; // valid
}));
$inputFilter = new \Zend\InputFilter\InputFilter();
$inputFilter->add($input);
$inputFilter->setData(array('fieldName' => 'value'));
var_dump($inputFilter->isValid()); // true, echoes 'called validator!'
$inputFilter->setData(array('fieldName' => ''));
var_dump($inputFilter->isValid()); // true, echoes 'called validator!'
$inputFilter->setData(array());
var_dump($inputFilter->isValid()); // false (null was passed to the validator)
If you also want to check against null
, you will need to extend Zend\InputFilter\Input
as following:
class MyInput extends \Zend\InputFilter\Input
{
// disabling auto-injection of the `NotEmpty` validator
protected function injectNotEmptyValidator() {}
}
continue_if_empty
solved my problem. Thanks to @dson-horácio-junior. This is what I used:
$this->add(array(
'name' => 'field',
'continue_if_empty' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim')
),
'validators' => array(
array(
'name' => 'Application\Form\Validator\Sample'
)
)
));
public function isValid($value, $context = null)
{
if ($value == '' && $context['otherfield'] == '') {
$this->error(self::INVALID_FIELD);
return false;
}
// ...
}
This triggered validation of my Callback
validator when the value was an empty string:
'required' => false,
'allow_empty' => false,
'continue_if_empty' => true,
'validators' => array(
array(
'name' => 'Callback',
'options' => array(
'callback' => function ($value, $context = []) use ($self) {
// ...
}
)
)
)
The allow_empty
initially invalidates the empty string and the continue_if_empty
allows it to then be evaluated by the validators that follow.
I see often the people making the mistake using allowEmpty
in the inputFilter config arrays. The string should be written with underscore separation not with camel case. So allow_empty
will work:
'fieldName' => array(
'name' => 'fieldName',
'required' => true,
'allow_empty' => true,
'filters' => array(
//... your filters ...
)
'validators' => array(
//... your validators ...
),
);
meaning a field with key 'fieldName' must be present in the data, but its value is allowed to be empty.
If you like to use a separate form validate class or a array notation for validate, you can do as follows:
$factory = new Zend\InputFilter\Factory();
$inputFilter = new Zend\InputFilter\InputFilter();
$inputFilter->add($factory->createInput(array(
'name' => 'name',
'required' => false,
'allowEmpty' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => '8',
'max' => '100',
),
),
),
)));
You can pass an array with required => false
and allowEmpty => true
to input filter factory (as I remember you can pass it directly to input filter too - not so sure).