13

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;
    }
    
Wilt
  • 41,477
  • 12
  • 152
  • 203
deyine
  • 201
  • 1
  • 2
  • 14
  • 1
    http://akrabat.com/zend-framework-2/setting-up-required-fields-that-can-be-empty-with-zendinputfilter/ – Sam Feb 16 '13 at 13:58
  • It's not what i need because if allow_empty = true and my element value is empty, zf2 consider that element is Valid and will not invoke validators. – deyine Feb 16 '13 at 14:29
  • I think i don't understand your problem then. Either you allow empty values or you don't. If you don't allow empty values, all validators will run, otherwise validation will fail, when an empty input is given. If you do allow them, an empty value doesn't need to be validated any further oO – Sam Feb 16 '13 at 15:29
  • Ok to simplify description, how can i, with zf2, applicate a custom validator on a field even if it's empty. – deyine Feb 16 '13 at 15:47
  • Simplifying isn't the way to go. What do you want to validate? `""` is empty - there is no validation for that. It's an empty string, what the heck do you want to validate with this? It just makes not the slightest sense. What EXACTLY do you want to do? – Sam Feb 16 '13 at 17:39
  • Thanks for your attention, I'm trying to migrate application from ZF1 to ZF2. I need to do validations for field A depending on the value of field B. I used http://www.jeremykendall.net/2008/12/24/conditional-form-validation-with-zend_form/ and it's required allow_empty=false for pass empty value to validator. On ZF2 this trick don't work because allow_empty = true add a NotEmptyValidator on top of ValidatorChain. – deyine Feb 16 '13 at 18:39
  • My solution is to create a custom Input class which overrides the injectNotEmptyValidator function, and forcing it to do nothing: `class CustomInput extends \Zend\InputFilter\Input { protected function injectNotEmptyValidator() { return; } }`
    This means that NotEmptyValidator will not added to my validator chain, so my validator can run correctly. All I have to do now is use my CustomInput class to populate InputFilter for element to validate. i'm not sure if it's the best way.
    – deyine Feb 16 '13 at 18:48

5 Answers5

27

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;
    }

    // ...
}
smottt
  • 3,272
  • 11
  • 37
  • 44
anilyeni
  • 744
  • 1
  • 8
  • 13
  • Best answer! Deserves more upvotes. If you set `continue_if_empty` to `true` then the `InputFilter\Factory` does not automatically add a `NotEmpty` validator, so all validation goes to the user defined validators. – kachar May 26 '15 at 10:45
  • i took 30 minutes to find this option THANKS a lot – Gizzmo Oct 28 '16 at 09:11
14

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() {}
}
Ocramius
  • 25,171
  • 7
  • 103
  • 107
  • You've go it,I want to check against null and empty, so your last solution confirms my implementation. Thanks a lot. – deyine Feb 16 '13 at 23:48
  • 5
    Just do `$input->setContinueIfEmpty(true);` instead of extending _Zend\InputFilter\Input_, it will have the same effect, less work and more simple. – Edson Horacio Junior Nov 19 '13 at 11:21
  • `NotEmpty::NULL` - this helped. For those who uses `fogetInputFilterSpecification()` with arrays you need the following structure: `'validators' => [[ 'name' => NotEmpty::class, 'options' => [ 'type' => NotEmpty::NULL ]]]'` – Zippp Jun 14 '18 at 10:45
7

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.

smottt
  • 3,272
  • 11
  • 37
  • 44
Onshop
  • 2,915
  • 1
  • 27
  • 17
2

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).

smottt
  • 3,272
  • 11
  • 37
  • 44
Ruwantha
  • 2,603
  • 5
  • 30
  • 44
2

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.

Wilt
  • 41,477
  • 12
  • 152
  • 203