Skip to content
This repository has been archived by the owner on Jan 30, 2020. It is now read-only.

How use Callback validator when field is conditionally required? #146

Open
vincequeiroz opened this issue Jul 31, 2017 · 3 comments
Open
Labels

Comments

@vincequeiroz
Copy link

I have two fields and one of them is required if the value of the other is equal to one condition.

How can I do this?

The "campaignFranchise" field is only required when the value of the "campaign" is equal to "franchise".

            4 => [
                'required' => true,
                'validators' => [
                    0 => [
                        'name' => NotEmpty::class,
                        'options' => [...],
                    ],
                    1 => [
                        'name' => StringLength::class,
                        'options' => [...],
                    ],
                ],
                'filters' => [...],
                'name' => 'campaign',
            ],
            5 => [
                'required' => true,
                'validators' => [
                    0 => [
                        'name' => NotEmpty::class,
                        'options' => [
                            'translatorenabled' => true,
                        ],
                        'break_chain_on_failure' => true,
                    ],
                    1 => [
                        'name' => Callback::class,
                        'options' => [
                            'callback' => [
                                CampaignFranchise::class,
                                'isValid',
                            ],
                        ],
                    ],
                    2 => [
                        'name' => StringLength::class,
                        'options' => [...],
                    ],
                ],
                'filters' => [...],
                'name' => 'campaignFranchise',
            ],

I try use "continueIfEmpty" and "allowEmpty" but how I don't have value and is required, the validation return false.
https://github.com/zendframework/zend-inputfilter/blob/master/src/Input.php#L410-415

If the field is required false, the validation does not trigger.
https://github.com/zendframework/zend-inputfilter/blob/master/src/BaseInputFilter.php#L251-L256

And if I use the callback validator in "campaign" field, the message error result display in wrong field.

@svycka
Copy link
Contributor

svycka commented Jul 31, 2017

hmm there is no feature for this I guess but you can use validationGroup and override isValid method of inputFilter something like this should work(not tested):

public function isValid($context = null)
{
    if ($this->getRawValue('campaign') !== 'franchise') {
        $validationGroup = array_keys($this->getInputs());
        unset($validationGroup['campaignFranchise']);
        $this->setValidationGroup($validationGroup);
    }
    return parent::isValid($context);
}

@rkeet
Copy link
Contributor

rkeet commented Jul 17, 2018

Example for you.

Use-case, an Entity can have child Entity Coordinates. Coordinates exists out of both latitude and longitude. Coordinates can only exist if both are present and must not be created if only one property is set.

class CoordinatesFieldsetInputFilter extends AbstractDoctrineFieldsetInputFilter
{
    public function init()
    {
        parent::init();

        $this->add([
            'name' => 'latitude',
            'required' => true,
            'allow_empty' => true,
            'filters' => [
                ['name' => StringTrim::class],
                ['name' => StripTags::class],
                [
                    'name' => ToNull::class,
                    'options' => [
                        'type' => ToNull::TYPE_STRING,
                    ],
                ],
                [
                    'name' => CallbackFilter::class,
                    'options' => [
                        'callback' => function ($value) {
                            $float = $value;

                            if ($value) {
                                $float = str_replace(',', '.', $value);
                            }

                            return $float;
                        },
                    ],
                ],
            ],
            'validators' => [
                [
                    'name' => StringLength::class,
                    'options' => [
                        'min' => 2,
                        'max' => 255,
                    ],
                ],
                [
                    'name' => CallbackValidator::class,
                    'options' => [
                        'callback' => function($value, $context) {
                            //If longitude has a value, mark required
                            if(empty($context['longitude']) && strlen($value) > 0) {
                                $validatorChain = $this->getInputs()['longitude']->getValidatorChain();

                                $validatorChain->attach(new NotEmpty(['type' => NotEmpty::NULL]));
                                $this->getInputs()['longitude']->setValidatorChain($validatorChain);

                                return false;
                            }

                            return true;
                        },
                        'messages' => [
                            'callbackValue' => _('Longitude is required when setting Latitude. Give both or neither.'),
                        ],
                    ],
                ],
            ],
        ]);

        $this->add([
            'name' => 'longitude',
            'required' => true,
            'allow_empty' => true,
            'filters' => [
                ['name' => StringTrim::class],
                ['name' => StripTags::class],
                [
                    'name' => ToNull::class,
                    'options' => [
                        'type' => ToNull::TYPE_STRING,
                    ],
                ],
                [
                    'name' => CallbackFilter::class,
                    'options' => [
                        'callback' => function ($value) {
                            $float = $value;

                            if ($value) {
                                $float = str_replace(',', '.', $value);
                            }

                            return $float;
                        },
                    ],
                ],
            ],
            'validators' => [
                [
                    'name' => StringLength::class,
                    'options' => [
                        'min' => 2,
                        'max' => 255,
                    ],
                ],
                [
                    'name' => CallbackValidator::class,
                    'options' => [
                        'callback' => function($value, $context) {
                            //If longitude has a value, mark required
                            if(empty($context['latitude']) && strlen($value) > 0) {
                                $validatorChain = $this->getInputs()['latitude']->getValidatorChain();
                                $validatorChain->attach(new NotEmpty(true));
                                $this->getInputs()['latitude']->setValidatorChain($validatorChain);

                                return false;
                            }

                            return true;
                        },
                        'messages' => [
                            'callbackValue' => _('Latitude is required when setting Longitude. Give both or neither.'),
                        ],
                    ],
                ],
            ],
        ]);
    }
}

@weierophinney
Copy link
Member

This repository has been closed and moved to laminas/laminas-inputfilter; a new issue has been opened at laminas/laminas-inputfilter#2.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

5 participants