Closed germanbisurgi closed 1 month ago
In your case, I would recommend using filters (you can pass parameters to filters).
Hi @sorinsarca, thanx for your reply,
this is cool but i notice that filters are a little limited. For example i can not display a custom error message. I was thinking more of creating a custom keyword class and then register it somehow into the validator. For Example this custom validator that sums every array item property "amount" and display an error if the sum is not 100:
<?php
namespace project\modules\frontend\helper;
use Opis\JsonSchema\{Keywords\ErrorTrait, ValidationContext, Keyword, Schema};
use Opis\JsonSchema\Errors\ValidationError;
class AmountSumKeyword implements Keyword
{
use ErrorTrait;
protected float $expectedSum = 100;
/**
* @inheritDoc
*/
public function validate(ValidationContext $context, Schema $schema): ?ValidationError
{
$data = $context->currentData();
if (!is_array($data)) {
return null;
}
$actualSum = 0;
foreach ($data as $item) {
if (is_object($item) && property_exists($item, 'amount') && is_numeric($item->amount)) {
$actualSum += $item->amount;
}
}
if ($actualSum === $this->expectedSum) {
return null;
}
return $this->error($schema, $context, 'amountSum', "The sum of the 'amount' properties must equal {expected}, but found {actual}", [
'expected' => $this->expectedSum,
'actual' => $actualSum,
]);
}
}
If you need a custom error message in the filter just throw a \Opis\JsonSchema\Errors\CustomError
exception.
Example
<?php
use Opis\JsonSchema\Validator;
use Opis\JsonSchema\Resolvers\FilterResolver;
use Opis\JsonSchema\Errors\CustomError;
$validator = new Validator();
/** @var FilterResolver $filters */
$filters = $validator->parser()->getFilterResolver();
$modulo = function (float $value, array $args): bool {
$divisor = $args['divisor'] ?? 1;
$reminder = $args['reminder'] ?? 0;
$ok = $value % $divisor == $reminder;
if (!$ok) {
throw new CustomError("My custom error message");
}
return true;
};
// Register our modulo filter
$filters->registerCallable("number", "modulo", $modulo);
In your case it should be something like this:
Schema
{
"type": "array",
"$filters": {
"$func": "check-sum",
"$vars": {
"sum": 42,
"error": "Sum of the items must be the answer to all"
}
}
}
Implementation
<?php
use Opis\JsonSchema\Validator;
use Opis\JsonSchema\Resolvers\FilterResolver;
use Opis\JsonSchema\Errors\CustomError;
$validator = new Validator();
/** @var FilterResolver $filters */
$filters = $validator->parser()->getFilterResolver();
$check_sum_filter= function (array $data, array $args): bool {
$actualSum = 0;
foreach ($data as $item) {
if (is_object($item) && property_exists($item, 'amount') && is_numeric($item->amount)) {
$actualSum += $item->amount;
}
}
$expectedSum = $args["sum"];
if ($expectedSum !== $actualSum) {
throw new CustomError($args["error"] ?? "Sum doesn't match", ["expected" => $expectedSum, "actual" => $actualSum]);
}
return true;
};
// Register our check-sum filter
$filters->registerCallable("array", "check-sum", $check_sum_filter);
LE: you can also have multiple filters
{
"$filters": [
{"$func": "a", "$vars": {"value": 1}},
{"$func": "b"}
]
}
Thank you, i'll try this tomorrow :-)
Great work by the way!
This works but have an issue. When adding $filters to a schema will trigger an error if the filter were not registered before validation. Is there a way to tell the validator to use the filter if it exists? if not, can this be implemented? I can try to make a Pull Request for this if necessary.
my schema:
{
"type": "number",
"$filters": {
"$func": "prime"
}
}
No, you must have the filters registered before parsing the schema. Is there any reason you can't register the filters 3 lines of code earlier?
In my case i am sending an AJAX request to an endpoint that performs the validation with Opis. I was testing the scenario where i'm using filters in the schema but without registering the filter in the validator. This results in an error that prevents the other validation errors to be send along in the response. It would be nice to have the behaviour where when the validator doesn't have the filter it just says it's fine because json-schema is permissive by default.
If you want to disable filters set allowFilters
to false (this will ignore the $filters keywords from schema) https://opis.io/json-schema/2.x/php-loader.html#parser-options
Otherwise, you'll have to register them.
This behavior will not change.
I understand, thanks for the help :-)
Is it possible to define custom validators / keywords and register then into the validator? I found out that it is possible to use custom formats but in my case i need to add a custom keyword alongside" format" Any examples or documentation about it? Thanx in advance :-)