-
Notifications
You must be signed in to change notification settings - Fork 11.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[9.x] Add ability to generate unique validation rules per nested array element (repush) #40498
[9.x] Add ability to generate unique validation rules per nested array element (repush) #40498
Conversation
In this example: 'items.*' => Rule::nested(function () {
return ['discounts.*.id' => 'distinct'];
}), What if you wanted to do something like |
We could do: 'items.*' => Rule::nested(function () {
return ['discounts.*.id' => ['distinct', 'another-rule']];
}), 'items.*' => Rule::nested(function () {
return ['discounts.*.id' => 'distinct|another-rule'];
}), Test:
|
$compiled->implicitAttributes, | ||
$this->implicitAttributes, | ||
[$attribute => [$key]] | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain this statement and why it is needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes absolutely:
I'm using array_merge_recursive
here so the implicitAttributes
arrays (along with their sub arrays) are merged together properly per iteration. Any additional implicit attribute absolute dot paths will be appended properly to each asterisk dot path.
I also make sure to merge the current implicit attribute dot path using: [$attribute => [$key]]
which is done in the else
statement below. This "segments" the implicit attributes into their own sections -- and from what I understand, the validator will perform validation on these independently, allowing us to use distinct
for example, to validate across the current array and not the entire array:
Example:
$rules = [
'items.*' => Rule::nested(function () {
return [
'discounts.*.id' => Rule::nested(function () {
return 'distinct';
}),
];
}),
];
$result = (new ValidationRuleParser(['...']))->explode($rules);
// {#65 ▼
// +"rules": array:5 [▼
// "items.0.discounts.0.id" => array:1 [▼
// 0 => "distinct"
// ]
// "items.0.discounts.1.id" => array:1 [▼
// 0 => "distinct"
// ]
// "items.0.discounts.2.id" => array:1 [▼
// 0 => "distinct"
// ]
// "items.1.discounts.0.id" => array:1 [▼
// 0 => "distinct"
// ]
// "items.1.discounts.1.id" => array:1 [▼
// 0 => "distinct"
// ]
// ]
// +"implicitAttributes": array:3 [▼
// "items.1.discounts.*.id" => array:2 [▼
// 0 => "items.1.discounts.0.id"
// 1 => "items.1.discounts.1.id"
// ]
// "items.0.discounts.*.id" => array:3 [▼
// 0 => "items.0.discounts.0.id"
// 1 => "items.0.discounts.1.id"
// 2 => "items.0.discounts.2.id"
// ]
// "items.*" => array:2 [▼
// 0 => "items.0"
// 1 => "items.1"
// ]
// ]
// }
@stevebauman just thinking out loud - what do you think about |
I like |
Also I forgot to mention @taylorotwell, should we be converting the Sometimes: $validator->sometimes('...', ['...'], function (Fluent $input) {
// ...
}); For Each: Rule::forEach(function ($attribute, Fluent $value, $data = null) {
// ...
}); |
@stevebauman doesn't $validator = Validator::make([
'users' => [
['id' => 1, 'email' => '[email protected]'],
['id' => 2, 'email' => 'foo bar'],
],
], [
'users.*.email' => Rule::forEach(function ($attribute, $value, $data) {
ray($value);
return ['email'];
}),
]); I think passing $validator = Validator::make([
'users' => [
['id' => 1, 'email' => '[email protected]'],
['id' => 2, 'email' => 'foo bar'],
],
], [
'users.*' => Rule::nested(function ($attribute, $value, $data) {
ray($value);
return ['email' => 'email'];
}),
]); |
Ah yea you're right. Guess we can't really convert it to anything since the data type will change |
Do you have any way to detect if the attribute Rule::foreEach is being applied to ends with a *? |
We could detect it via the $rules = [
'items.*' => Rule::nested(function () {
return ['discounts.*.id' => 'distinct'];
}),
];
$results = (new ValidationRuleParser)->explode($rules);
// ...
// explodeWildcardRules()...
if ($rule instanceof NestedRules) {
$arrayable = Str::afterLast($attribute, '.') === '*';
$compiled = $rule->compile($key, $arrayable ? new Fluent($value) : $value, $data);
// ...
} else { |
What do you think about that? Good idea? |
Ah, not too sure after some thought. This wouldn't make sense to use a $data = ['items' => [1,2,3]];
$rules = [
'items.*' => Rule::nested(function ($attribute, Fluent $value) {
return 'distinct';
}),
];
$data = ['items' => [1,2,3]];
Rule::nested(function ($attribute, Collection $value) {
// ...
}, function ($value) {
return collect($value);
});
We can alter the behaviour in a next major version if developers really see it as a need. |
I think we should swap the argument order though since the Rule::forEach(function ($value, $attribute) {
// ...
});
// vs
Rule::forEach(function ($attribute, $value) {
// ...
}); |
Yeah, I'm fine with swapping the argument order. |
Ok, argument order has been swapped! 👍 |
@stevebauman why did not you rename this rule from |
Description
This PR introduces the ability to generate unique validation rules per nested array element, granting you direct access to each value supplied the request, the attribute name, and the whole data set in case other attribute values are needed.
This will be done using a new
Rule
static method:Rule::nested($callback)
.Purpose
Generation of unique validation rules in nested arrays is currently pretty difficult, especially if you use any sort of custom validation rules. There is no built in way to generate validation rules taking into account the current iteration's value, and you'll have to set the indexes and such manually inside of a
$rules
array prior to sending it to the validator.This also becomes quite problematic because we need to trust the data structure that was sent to us in the request and then iterate over it to dynamically generate our rules prior to actual validation taking place.
Example
Before:
After:
Another example where this is extremely handy is the validation of complex nested relationships:
You may also supply an array of rules as you would normally with
nested
instances, as well as supply multiplenested
instances:Validation of arrays is super tricky stuff. I think this greatly simplifies the developer experience, allowing us not to think too much about its complexities.
Please let me know if you have any questions/comments/concerns. Thanks so much for your time! ❤️
Apologies for having to re-push this -- my git skills are not the greatest 😅