Reputation: 3536
i need to do the following conditional validation in my modelless form:
class NewForm extends Form
{
if (address_type == 'Street')
required: street_name, street_type
not required: po_box
elseif (address_type == 'PO')
required: po_box
not required: street_name, street_type
}
I have tried the following validator:
$validator
->requirePresence('street_name', true, 'street_name is a required field')
->notEmpty('street_name', 'street_name is a required field', function ($context) {
return ($context['address_type'] == 'Street Address');
})
->requirePresence('street_type', true, 'street_type is a required field')
->notEmpty('street_type', 'street_type is a required field', function ($context) {
return ($context['address_type'] == 'Street Address');
});
$validator
->requirePresence('po_box', true, 'po_box is a required field')
->notEmpty('po_box', 'po_box is a required field', function ($context) {
return ($context['address_type'] == 'PO');
});
When i call validate like such
$valid = $newform->validate($data);
It requires ALL street_name, street_type, and po_box to be in the input data.
How do i structure the conditional validator to accomplish the pseudocode ? Thanks
Edit
I've come up with the following, which works ! However, it does not allow me get the fields that are empty/invalid from the ->errors() Ie, i need to be able to know that, for eg, 'street_name' was empty and needs to be filled in.
$validator
->add('address_type', 'missing fields', [
'rule' => function ($value, $context) {
if ($value == 'Street') {
if ((!array_key_exists('street_name', $context['data']) && empty($context['data']['street_name'])) ||
(!array_key_exists('street_type', $context['data']) && empty($context['data']['street_type']))) {
return false;
}
} elseif ($value == 'PO') {
if ((!array_key_exists('po_box', $context['data']) && empty($context['data']['po_box'])) ||
(!array_key_exists('po_no', $context['data']) && empty($context['data']['po_no'])) ||
(!array_key_exists('po_exchange', $context['data']) && empty($context['data']['po_exchange']))) {
return false;
}
}
}
]);
Upvotes: 0
Views: 414
Reputation: 60453
It would come handy if Validator::requirePresence()
would support callables for conditional evaluation, just like add()
, allowEmpty()
and noteEmpty()
do.
Until something like this is added, there are various other ways to handle this, like for example modifying validators in a custom rule for your address_type
field, in an overriden Form::validate()
method, or even in the controller.
I'd probably go with the first variant, something like this, which sets the fields to be required depending on the required address_type
value:
protected function _buildValidator(Validator $validator)
{
return $validator
->requirePresence('address_type')
->add('address_type', 'valid', [
'rule' => function($value) {
switch ($value) {
case 'Street Address':
$this->validator()
->requirePresence('street_name')
->requirePresence('street_type');
break;
case 'PO':
$this->validator()
->requirePresence('po_box');
break;
default:
return false;
}
return true;
}
])
->notEmpty('street_name')
->notEmpty('street_type')
->notEmpty('po_box')
// ...
;
}
Update
Support for callables in requirePresence()
has been added for 3.1.1. With that functionality it's possible to conditionally require fields like
$validator
// ...
->requirePresence('street_name', function ($context) {
return $context['data']['address_type'] === 'Street Address';
})
// ...
;
To DRY up things in case of many fields one could for example make of use dynamic callable arguments like
$requiredFor = function ($value) {
return function ($context) use ($value) {
return $context['data']['address_type'] === $value;
};
};
$validator
// ...
->requirePresence('street_name', $requiredFor('Street Address'))
->requirePresence('street_type', $requiredFor('Street Address'))
->requirePresence('po_box', $requiredFor('PO'))
// ...
;
See also Cookbook > Validation > Conditional Validation
Upvotes: 2