-
Notifications
You must be signed in to change notification settings - Fork 30
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 support for validation of optional data structures #35
Comments
Hi @thibaudcolas. First of all thanks for using Slim-Validation! It seems that your case is not handled in the way we expect by Respect/Validation itself if you only use the keySet rule. I tried this code to verify what I'm saying: use Respect\Validation\Exceptions\NestedValidationException;
use Respect\Validation\Validator as v;
$request = [
'payer' => [
'id' => 123,
'company' => []
]
];
try {
v::keySet(
v::key('payer', v::keySet(
v::key('id', v::numeric()),
v::key('company', v::keySet(
v::key('id', v::numeric())
), false)
))
)->assert($request);
echo "Everything is ok!";
} catch (NestedValidationException $exception) {
print_r($exception->getMessages());
} but the printed error is this one:
that doesn't explicit tell you what is the position of the id (under In Slim-Validation you can use the optional rule to obtain your expected result: $validators = [
'payer' => [
'id' => self::idValidator('must be a valid id'),
'company' => v::optional(v::keySet(
v::key('id', self::idValidator('must be a valid id'))
))
],
]; In the case your input is: {
"payer" : {
"id": 123,
"company": {}
}
} you'll get the given error: [
'payer.company' => [
'Must have keys { "id" }',
]
] I hope I have answered your question. |
Thank you for taking the time to look into this 🙂. I ended up doing exactly what you suggests, it works for that example but breaks down as soon as there is more nesting: {
"payer" : {
"id": 123,
"company": {
"contact": {
"id": 123
}
}
}
} Here, if I want to validate that I converted my validator into something like: $validators = [
'payer' => [
'id' => v::optional(self::idValidator()),
'consumer' => v::optional(self::silentAllOf(
v::key('id', self::idValidator()),
v::key('nationality', self::countryCodeValidator())
)),
],
]; That works the same as Investigating further, trying out both the middleware and the validation library on its own, I believe this is simply a fundamental limitation of Coming back to the middleware, what was problematic for me is that grouping errors by key ( Trying to find a way to solve this, I think it might be possible to change the middleware to:
Iterating through the input's structure is easy enough, the hard part is having an API in the middleware that makes it possible to set validators for any path in the input. As a thought experiment, trying to keep backwards compatibility with the current API, defining validators could look something like this: (explanation below) $validators = [
'payer' => [
'company?' => [
'__self__' => v::keySet(v::key('id'), v::key('contact', false)),
'contact?' => [
'id' => v::intType(),
'name' => v::stringType(),
],
'emails' => [
v::email(),
],
'phoneNumbers' => [
'__self__' => v::arrayType(),
[
'countryCode' => v::stringType(),
'number' => v::phone(),
],
],
],
],
];
I'm sure there are cases I'm forgetting about, but an API like this would make it possible to define (and resolve) validators for paths like I might try and implement this some time, but time is scarce and my PHP skills are quite poor so we'll see 😄. |
Wow! 😮 Your comment adds a lot of useful things. I think the solution you are proposing is really interesting and I'd be curious to see a pull request in this regard. Right now I do not think there are any problems but only a lot of tests in various scenarios could help us to understand it (e.g. what happens if you don't want that your input contains more keys at |
:) I’ll try to make a proof of concept if I find time. It might not be too much work if I manage to overcome my PHP struggles. |
If it is originally json you want to validate, you could also use https://github.com/justinrainbow/json-schema. You can for example feed it a schema to validate against like this: {
"type": "object",
"properties": {
"insert": {"type": "string"},
"attributes": {"type": "object"}
},
"required": ["insert"],
"additionalProperties": false
} I made a custom rule to work with this: class JsonSchema extends AbstractRule {
/** @var \JsonSchema\Validator **/
public $validator;
/** @var string **/
protected $schema;
/**
* @param string $schemaPath
*/
public function __construct($schemaPath) {
$this->validator = new \JsonSchema\Validator();
$this->schema = [
'$ref' => 'file://'.$schemaPath,
];
}
public function validate($input) {
if (is_string($input)) {
$input = json_decode($input);
}
$this->validator->validate($input, $this->schema);
return $this->validator->isValid();
}
} |
@lode This is really interesting! What do you think if you publish this rule as a separate package? I would be more than happy to depend on it if it simplify things. 😄 |
The rule is just the code shown above. The actual validation is done by https://github.com/justinrainbow/json-schema. Further the specifics of this class depend a lot on how you want to use it. E.g. in my context I'm passing a I think it is hard to make a generic package, maybe it works better in a Slim context. But I don't know Slim well enough, feel free to use the example to make something useful! |
I'm trying and struggling to get the middleware working when validating data structures using optional objects. Here is an example of a JSON payload I'm working with:
The tricky part here is with
payer.company
: I would like this to be optional – i.e. validate its attributes if it's there, but otherwise apayer
with just anid
is valid too. I haven't found a good way to do this with the way the middleware defines validators. The correctrespect/Validation
way would be to usekeySet
andkey
:This produces the following error list when
payer.company
is an empty object:The error message is what I would expect, but since the middleware operates with a validator defined on
payer
, the messages' key set to "payer" does not point to what is causing the error. Generally, anything defined as akeySet
causes this loss in accuracy of the error keys, since Slim-Validation has no awareness of how validators are defined within akeySet
.This might not be very problematic for a simple structure like this, but of course my real-world use case has a much bigger payload and more levels of nested objects. Is there anything I could do differently to make this work?
The text was updated successfully, but these errors were encountered: