-
Notifications
You must be signed in to change notification settings - Fork 771
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
Improve messages in an array format #1498
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
<?php | ||
|
||
/* | ||
* Copyright (c) Alexandre Gomes Gaigalas <[email protected]> | ||
* SPDX-License-Identifier: MIT | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
test('https://github.com/Respect/Validation/discussions/1427', expectAll( | ||
fn () => v::each( | ||
v::arrayVal() | ||
->key('groups', v::each(v::intVal())) | ||
->key('permissions', v::each(v::boolVal())) | ||
) | ||
->assert([ | ||
16 => [ | ||
'groups' => [1, 'A', 3, 4, 5], | ||
'permissions' => [ | ||
'perm1' => true, | ||
'perm2' => false, | ||
'perm3' => 'boom!', | ||
], | ||
], | ||
18 => false, | ||
24 => ['permissions' => false], | ||
]), | ||
'`.16.groups.1` must be an integer value', | ||
<<<'FULL_MESSAGE' | ||
- Each item in `[16: ["groups": [1, "A", 3, 4, 5], "permissions": ["perm1": true, "perm2": false, "perm3": "boom!"]], 18: false, ... ]` must be valid | ||
- `.16` must pass the rules | ||
- Each item in `.groups` must be valid | ||
- `.1` must be an integer value | ||
- Each item in `.permissions` must be valid | ||
- `.perm3` must be a boolean value | ||
- `.18` must pass all the rules | ||
- `.18` must be an array value | ||
- `.groups` must be present | ||
- `.permissions` must be present | ||
- `.24` must pass the rules | ||
- `.groups` must be present | ||
- `.permissions` must be iterable | ||
FULL_MESSAGE, | ||
[ | ||
'messages' => ['each' => 'Each item in `[16: ["groups": [1, "A", 3, 4, 5], "permissions": ["perm1": true, "perm2": false, "perm3": "boom!"]], 18: false, ... ]` must be valid'], | ||
'children' => [ | ||
16 => [ | ||
'messages' => ['allOf' => '`["groups": [1, "A", 3, 4, 5], "permissions": ["perm1": true, "perm2": false, "perm3": "boom!"]]` must pass the rules'], | ||
'children' => [ | ||
'groups' => [ | ||
'messages' => ['each' => 'Each item in `[1, "A", 3, 4, 5]` must be valid'], | ||
'children' => [ | ||
1 => [ | ||
'messages' => ['intVal' => '"A" must be an integer value'], | ||
], | ||
], | ||
], | ||
'permissions' => [ | ||
'messages' => ['each' => 'Each item in `["perm1": true, "perm2": false, "perm3": "boom!"]` must be valid'], | ||
'children' => [ | ||
'perm3' => [ | ||
'messages' => ['boolVal' => '"boom!" must be a boolean value'], | ||
], | ||
], | ||
], | ||
], | ||
], | ||
18 => [ | ||
'messages' => ['allOf' => '`false` must pass all the rules'], | ||
'details' => ['arrayVal' => '`false` must be an array value'], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added this key because some rules have children but they're respective to the parent validation, not a child. I thought I would add an extra key there to identify those. I think that one would always use |
||
'children' => [ | ||
'groups' => [ | ||
'messages' => ['keyExists' => '`false` must be present'], | ||
], | ||
'permissions' => [ | ||
'messages' => ['keyExists' => '`false` must be present'], | ||
], | ||
], | ||
], | ||
24 => [ | ||
'messages' => ['allOf' => '`["permissions": false]` must pass the rules'], | ||
'children' => [ | ||
'groups' => [ | ||
'messages' => ['keyExists' => '`["permissions": false]` must be present'], | ||
], | ||
'permissions' => [ | ||
'messages' => ['each' => '`false` must be iterable'], | ||
], | ||
], | ||
], | ||
], | ||
] | ||
henriquemoody marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I gotta say, we can added a whole bunch of other things here, I'm just not sure how they can be useful. Each element could contain the keys:
But when I start to really think about this, I think this is not even an array in an exception anymore, it could be an object that contains all this information and handles all of them properly. Maybe I'm just thinking of providing this array to the exception because of how the library worked before, but I could just drop the whole array thing, and return a proper object in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can offer some perspective on what I was thinking when I made the original library. The array thing and a lot of things (like setting properties in runtime objects) were implementation details, mostly private stuff, that could be done in whatever way fits best (I decided to go for the solution that had lass code back then). To me, the
AbstractNestedException had things like The decision to use exceptions also played a role in the design of So, you're still dealign with a lot of interlocked design decisions I made back then :D. You don't need to follow any of them if they don't make sense anymore. I would just go for whatever fullfills your vision best. That was what I did: I just moved things around until it was cohese. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for giving me some perspective, @alganet, also it's always nice to hear from you! That does give me more peace with changing things a bit more, although at this point, only the API of the previous I think that failing fast is still important in some cases. We'll have Circuit in version 3.0 to fulfil that purpose, but users will need to be intentional about it. One thing that I liked a lot about Since you're here, I'd love to get your perspective on something else. When you created the library were the validation messages meant for engineers or end-users? That's something that I'm often conflicted about. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought of use cases for both engineers and end-users. The Some of the features were designed to later work with APIs as well. That's why The So, in a sense, I did for both, but I always had some specific use cases in mind. The way I perceive what you're changing is to move to a canonical, fully serializable "report". Mine was different, in my idea you could "query" this non-linear exception tree for a specific serialization. There are compromises in both, and I don't prefer any over the other, both are cool. One thing that bothered me in my original design was that the validation tree and the result tree were sometimes not the same. I discovered too late that these trees were not equal. I never truly figured this part out, and the API grew from the assumption that these two trees (the composite validator and the result exception) were "equivalent". However, several objects "knew inside" that there were violations to that equivalency. I sometimes see you wrestling with similar problems, but I honestly don't have much to add :) I think it's an unsolvable problem, and you'll do similar in spirit to what I did (just trim the rough edges as best as possible), but with a more robust implementation design. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What you mentioned about the trees is so true... It got a bit better with the As for this specific change connected to your last comment, just as you, I also thought of Good to hear your thoughts. It was also good to be reminded of the I'm thinking of repurposing the $failures = v::key('foo', v::intVal()->positive()->greaterThan())->validate($input);
if ($failures === null) {
echo 'No failures' . PHP_EOL;
return;
}
// Getting all messages
echo $failures->getMessage() . PHP_EOL;
echo $failures->getFullMessage() . PHP_EOL;
echo print_r($failures->getMessages(), true) . PHP_EOL;
// Finding specific messages
echo $failures->findMessage('foo') . PHP_EOL;
echo print_r($failures->findMessages(['foo' => 'Something went wrong']), true) . PHP_EOL; Thoughts? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it looks cool! I would use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought about that too, and I suggested returning the failures because I think it's no use for the user to deal with anything but the failures, and consequently no reason for this object to contain passing results. Apart from that, the I do get what you're saying about interfaces that return nullable objects. Maybe something in-between could work: $failures = v::key('foo', v::intVal()->positive()->greaterThan())->validate($input);
if (count($failures) === 0) {
echo 'No failures' . PHP_EOL;
return;
} It looks very similar to Symfony Validator 🙃 But alternatively, it could be something like this: $failures = v::key('foo', v::intVal()->positive()->greaterThan())->validate($input);
if ($failures->isEmpty()) {
echo 'No failures' . PHP_EOL;
return;
} What do you think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like it (the latest snippet)! 🐼 |
||
)); |
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.
I'm not replacing the input of each rule with the key, because I think that, since the structure is already in the array, it's best to keep the original message instead, so you can actually see the value that failed.