According to the docs for the required rule, "A field is considered "empty" if... the value is null." However, the required_if and required_unless rules no longer allow "empty" fields to have a value of null if you are also validating a particular type. This seems to be a regression from version L5.2.
Say the data you're validating has two properties, some_bool and optional_value, and you want optional_value to be a boolean, and to be required only if some_bool is true, but otherwise it can be null. In L5.2, you could easily accomplish this using the boolean and required_if rules:
'optional_value' => 'boolean|required_if:some_bool,true'
After upgrading to L5.3, this no longer produces the correct result if some_bool = false and optional_value = null - in this case, it should be valid, but instead it flags the field as invalid ("The optional value field must be true or false.").
My first thought was to add the nullable rule:
'optional_value' => 'boolean|required_if:some_bool,true|nullable'
But this does not produce the desired result if some_bool = true and optional_value = null - it should be invalid, but it allows the field to pass as valid.
Is there any way to correctly validate an optional value to be a particular type where null is considered to be empty?
I could write my own custom rules, but that sounds like about as much fun as re-inventing the wheel, especially since this used to be possible in L5.2.
This passes for me on latest 5.3:
$validator = Validator::make(
[
"some_bool" => false,
"optional_value"=> null
],
[
'optional_value' => 'required_if:some_bool,true'
]);
Is that the case that fails for you?
@themsaid Thanks for pointing that out. I realized my explanation of the problem was incomplete. The problem involves validating optional_value to be a particular type (e.g., boolean) and _also_ using the required_if rule. I have updated my question accordingly.
I experienced the same issue on required_with rules combined with nullable.
by using 'optional_value' => 'boolean' you say it hsould be true or false, if you add nullable you say it's ok to be null, so when you say it's required but can be null the validator considers null as a valid value.
please refer to https://github.com/laravel/framework/issues/16646, this topic was discussed there and the conclusion is that such case of conditional validation requires the usage of sometimes(), the $validator->sometimes() method was built to cover these cases.
Closing this since it'a duplicate of another issue, please refer to the other thread. If you need any further help feel free to ping me.
Here's a hint for what you can do:
$validator = Validator::make(
[
"some_bool" => true,
"optional_value" => null
],
[
'optional_value' => 'boolean|required_if:some_bool,true'
]);
$validator->sometimes('optional_value', 'nullable', function ($data) {
return ! $data->get('some_bool');
});
Re-opening this issue so that others can see where we reach.
Thanks for the link to the existing issue. (I had searched for related issues, but didn't find that one.)
I'm having trouble figuring out how I would implement the sometimes rule as described, since I do not have any access to the validator instance at the point where the validation rules are defined. The validator instance is created and immediately run within an inherited validate method (very similar to using validation in a controller). Do I have to override the validate method in order to use this approach or is there a less intrusive way?
You'll need an instance of the validator to be able to use the sometimes() method, you can use the helper method validator() to easily create a validator instance.
I don't see anything about a validator() helper function in the docs. What are you referring to exactly? Besides, if the function creates a new Validator instance, that wouldn't be the same instance that the controller's validate() method uses, so I don't see how that would help. Can you explain in more detail and/or provide an example for me to look at? Thanks.
Check this trait, Illuminate\Foundation\Validation\ValidatesRequests, it contains the methods used in the controller for dealing with the validator.
I don't understand why you want to use the same instance, you can just create a new validator and use the sometimes() method as you wish, you don't have to use the methods in this trait.
Closing this issue since it's not a bug in the framework, you may ask questions on the forums or the #questions channel of Larachat.
Maybe I'm just being dense, but I don't see a validator() method in Illuminate\Foundation\Validation\ValidatesRequests either. The closest thing I see is getValidationFactory(), which you can use to create a new Validator instance by calling the factory's make() method. Is that what you meant?
Anyways, I just want to make sure I'm understanding what you're suggesting as a workaround, both for my benefit and for anyone else who looks at this issue. You said:
I don't understand why you want to use the same instance, you can just create a new validator and use the sometimes() method as you wish, you don't have to use the methods in this trait.
This implies that you cannot use the sometimes() method and also use the controller's default validate() method to perform the validation. Is that correct?
If so, it sounds like you're suggesting overriding/re-implementing the validate() method that the controller inherits from ValidatesRequests in order to add a call to the sometimes method. That is, when you put everything together, your controller would need to have something like this:
$rules = [
'optional_value' => 'sometimes|boolean|required_if:some_bool,true',
];
$validator = $this->getValidationFactory()->make($request->all(), $rules); // or Validator::make()?
$validator->sometimes('optional_value', 'nullable', function ($data) {
return !is_null($data->some_bool); // implicitly calls `$data->get('some_bool')`
});
if ($validator->fails()) {
$this->throwValidationException($request, $validator);
}
I think that would work, but that's a lot of additional code (especially when there are a number of places I need to do this), and I would consider this a very inconvenient loss of functionality from L5.2, when it was possible to handle the case I described by simply using the required_if rule. :disappointed:
Seems a little hacky, but I found it's way easier to do this instead:
$rules = [
'optional_value' => 'boolean',
];
$rules['optional_value'] .= $request->input('some_bool') ? '|required' : '|nullable';
$this->validate($request, $rules);
Most helpful comment
Seems a little hacky, but I found it's way easier to do this instead: