Search code examples
validationcakephpcakephp-3.x

How to have a CakePHP model field requirement checked manually?


QUESTION UPDATED: I found out more information and therefor changed my question.

I want to save my user with his license number, but only if he selects that he has a license. I have three possible values for the pilot_type field (ROC, ROC-light and recreative) and only the last option should allow for an empty string to be submitted.

In order to achieve this, I wrote the following validation function:

$validator
    ->add('license_nr', 'present_if_license', [
        'rule' => function ($value, $context) {
            return $context['data']['pilot_type'] == 'recreative' || !empty($value);
        },
        'message' => "If you don't fly recreatively, a license number needs to be supplied"
]);

The problem is that setting any validation rule on a field will trigger an additional check in the CakePHP Model class that will reject the value if it's empty. I tried to fix this by adding ->allowEmpty('license_nr'), but that rule makes for the model to accept an empty string without even running my custom function. Even putting them in order and using 'last' => true on my custom rule doesn't resolve this problem:

$validator
    ->add('license_nr', 'present_if_license', [
        'rule' => function ($value, $context) {
            return false;
            // return $context['data']['pilot_type'] == 'recreative' || !empty($value);
        },
        'last' => true,
        'message' => "If you don't fly recreatively, a license number needs to be supplied"
    ])
    ->allowEmpty('license_nr');

How do I make CakePHP run my custom function in order to see if the field can be empty, rather than just assuming that it can never be empty or must always be empty?


Solution

  • By default fields aren't allowed to be empty, so that's the expected behavior and you'll have to explicitly allow empty values.

    In order to achieve what you're trying to do, instead of using an additional rule, you should customize the allowEmpty() rule, use the second argument to supply a callback (note that unlike rule callbacks it will receive a single argument that provides the context).

    So you'd do something like this, which you may need to modify a bit, for example depending on whether you need it to work differently for creating ($context['newRecord'] = true) and updating ($context['newRecord'] = false) records:

    ->allowEmpty(
        'license_nr',
        function ($context) {
            return $context['data']['pilot_type'] === 'recreative';
        },
        "If you don't fly recreatively, a license number needs to be supplied"
    )
    

    As of CakePHP 3.7 you can use allowEmptyString(), it will work the same way, you just need to swap the second and third arguments, as allowEmptyString() takes the message as the second argument, and the callback as the third argument.

    See also