I have read a couple of Stack overflow questions about nested array but I want something a step further. Lets say something like the code below:
I have a validator snippet where the request takes an array. Based on what I understood, on xxx.*.yyy
, it will find all array of xxx
with yyy
as key and validate them . However, what if I want to validate the image_id
and image_path
based on the value of type
?
Note: type, title, image_id and image_path are all on the same level.
The array that I want to validate is like this:
[contents] => Array
(
[0] => Array
(
[type] => not_image
[title] => title
)
[1] => Array
(
[type] => image
[image_id] => 13
[image_path] => some_image_string
)
)
)
while my validator is this:
$v = Validator::make($request->all(), [
'contents.*.type' => ['string', 'required', Rule::in(['image', 'not_image']),],
'contents.*.image_id' => ['required', 'integer'],
'contents.*.image_path' => ['required', 'string', 'min:2', 'max:500'],
]);
Only validated image_id and image_page if type is image where image_id and image_path would only be validated if type is validated. I know that the code is missing that one ingredient to do what I want. If you have other ideas on how to do this, that would also be great.
CodePudding user response:
Your last code is not valid, you are saying type => string, required, rule::in, image_id => [rules]
, that is not valid, that post you are following is just badly indented...
There is a specific section in the documentation that talks and shows about this...
The rule you have to use is exclude_if
, the documentation states:
You may occasionally wish to not validate a given field if another field has a given value. You may accomplish this using the
exclude_if
validation rule. In this example, theappointment_date
anddoctor_name
fields will not be validated if thehas_appointment
field has a value offalse
And the example they give is:
use Illuminate\Support\Facades\Validator;
$validator = Validator::make($data, [
'has_appointment' => 'required|boolean',
'appointment_date' => 'exclude_if:has_appointment,false|required|date',
'doctor_name' => 'exclude_if:has_appointment,false|required|string',
]);
As you can see, if the has_appointment
is false
(exclude_if:has_appointment,false
), then appointment_date
and doctor_name
will not be validated.
In your case, it should be like:
Validator::make(
$request->all(),
[
'contents.*.type' => ['string', 'required', Rule::in(['image', 'not_image'])],
'contents.*.image_id' => ['exclude_unless:contents.*.type,image', 'required', 'integer'],
'contents.*.image_path' => ['exclude_unless:contents.*.type,image', 'required', 'string', 'min:2', 'max:500'],
'contents.*.title' => ['exclude_unless:contents.*.type,not_image', 'required', 'string'],
]
);
What that validator is going to do is:
- Only when
contents.*.type = image
, thenimage_id
, andimage_path
are going to be validated - Only when
contents.*.type = non_image
, thentitle
is going to be validated
See that I have used the opposite of exclude_if
, that is exclude_unless
. So we are saying exclude_unless:contents.*.type,image
=> If contents.*.type
IS image
, then use whatever rules I have after this, else completely ignore this field.
You can write it using exclude_if
and it would be like this:
Validator::make(
$request->all(),
[
'contents.*.type' => ['string', 'required', Rule::in(['image', 'not_image'])],
'contents.*.image_id' => ['exclude_if:contents.*.type,non_image', 'required', 'integer'],
'contents.*.image_path' => ['exclude_if:contents.*.type,non_image', 'required', 'string', 'min:2', 'max:500'],
'contents.*.title' => ['exclude_if:contents.*.type,image', 'required', 'string'],
]
);
The difference (appart from exclude_if
instead of exclude_unless
) is that now you are ONLY excluding if image
or non_image
, but using exclude_unless
is saying: Only validate if image
(or non_image
), so if you have 3 types instead of 2 (image
and non_image
), exclude_if
will not work, because you can't specify more than 1 value.
Another quick tip is: just use $request->validate()
instead of Validator::make
:
$request->validate([
'contents.*.type' => ['string', 'required', Rule::in(['image', 'not_image'])],
'contents.*.image_id' => ['exclude_unless:contents.*.type,image', 'required', 'integer'],
'contents.*.image_path' => ['exclude_unless:contents.*.type,image', 'required', 'string', 'min:2', 'max:500'],
'contents.*.title' => ['exclude_unless:contents.*.type,not_image', 'required', 'string'],
]);