From 4abe3e1802ea8e952aaf58d735fa891a76bf8c30 Mon Sep 17 00:00:00 2001 From: Tom Janssen Date: Tue, 5 Aug 2025 14:33:10 +0200 Subject: [PATCH] add rule --- CHANGELOG.md | 3 ++ README.md | 4 +- src/Contracts/JsonSchemaNameProvider.php | 8 +++ src/Rules/BaseRule.php | 44 +++++++++++++++++ src/Rules/JsonSchemaAttributeRule.php | 62 ++++++++++++++++++++++++ src/Rules/JsonSchemaRule.php | 27 ++--------- 6 files changed, 124 insertions(+), 24 deletions(-) create mode 100644 src/Contracts/JsonSchemaNameProvider.php create mode 100644 src/Rules/BaseRule.php create mode 100644 src/Rules/JsonSchemaAttributeRule.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cd0250..736cf10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## 2.1.0 - 2025-08-05 +- Add `JsonSchemaAttributeRule` + ## 2.0.0 - 2025-08-05 - Modernize code - Drop support for laravel 10.x diff --git a/README.md b/README.md index 286b8ff..7f56a61 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,9 @@ composer require dutchcodingcompany/laravel-json-schema ## Usage 1. Create a json schema in the schema directory, eg. `storage/app/schema/example.json` -2. Reference the schema in the validator: `new \DutchCodingCompany\LaravelJsonSchema\Rules\JsonSchemaRule('example')` +2. Reference the schema in the validator using one of the rules: + - `new \DutchCodingCompany\LaravelJsonSchema\Rules\JsonSchemaRule('example')` + - `new \DutchCodingCompany\LaravelJsonSchema\Rules\JsonSchemaAttributeRule('type', TypeEnum::class)` (be careful to validate the attribute `type` in this case) To customize the schema directory, use environment variable `JSON_SCHEMA_DIRECTORY` or publish the config file. diff --git a/src/Contracts/JsonSchemaNameProvider.php b/src/Contracts/JsonSchemaNameProvider.php new file mode 100644 index 0000000..42cfe41 --- /dev/null +++ b/src/Contracts/JsonSchemaNameProvider.php @@ -0,0 +1,8 @@ +schemaValidator = $schemaValidator ?? Container::getInstance()->make(JsonSchemaValidator::class); + } + + /** + * Run the validation rule. + */ + public function validate(string $attribute, mixed $value, Closure $fail): void + { + $result = $this->schemaValidator->validate($this->determineSchemaName(), $value); + + if ($result->failed()) { + $fail($this->detailedMessage + ? 'json-schema::messages.detailed-error-message' + : 'json-schema::messages.error-message' + )->translate([ + 'attribute' => $attribute, + 'details' => $result->getMessage() ?? '', + ]); + } + } + + abstract protected function determineSchemaName(): string; +} diff --git a/src/Rules/JsonSchemaAttributeRule.php b/src/Rules/JsonSchemaAttributeRule.php new file mode 100644 index 0000000..20be424 --- /dev/null +++ b/src/Rules/JsonSchemaAttributeRule.php @@ -0,0 +1,62 @@ + + */ + protected $data = []; + + /** + * @param string $attribute from with other attribute to retrieve the schema name + * @param class-string<\BackedEnum&\DutchCodingCompany\LaravelJsonSchema\Contracts\JsonSchemaNameProvider>|null $enum which enum to cast the schema name into (if applicable) + */ + public function __construct( + protected string $attribute, + protected ?string $enum = null, + protected bool $detailedMessage = true, + JsonSchemaValidator | null $schemaValidator = null, + ) { + parent::__construct($detailedMessage, $schemaValidator); + } + + /** + * Set the data under validation. + * + * @param array $data + */ + public function setData(array $data): static + { + $this->data = $data; + + return $this; + } + + protected function determineSchemaName(): string + { + $schemaName = data_get($this->data, $this->attribute); + + if ($this->enum !== null) { + $enum = ($this->enum)::tryFrom($schemaName); + + if ($enum instanceof JsonSchemaNameProvider) { + return $enum->getSchemaName(); + } + } + + if (is_string($schemaName)) { + return $schemaName; + } + + throw new InvalidArgumentException('Cannot determine schema name from attribute "'.$this->attribute.'"'); + } +} diff --git a/src/Rules/JsonSchemaRule.php b/src/Rules/JsonSchemaRule.php index 94e2dfd..6ebee5f 100644 --- a/src/Rules/JsonSchemaRule.php +++ b/src/Rules/JsonSchemaRule.php @@ -4,41 +4,22 @@ use Closure; use DutchCodingCompany\LaravelJsonSchema\Contracts\JsonSchemaValidator; -use Illuminate\Container\Container; -use Illuminate\Contracts\Validation\ValidationRule; -class JsonSchemaRule implements ValidationRule +class JsonSchemaRule extends BaseRule { - protected JsonSchemaValidator $schemaValidator; - /** * @param string $schema name of the schema to validate - * @param bool $detailedMessage wether of not to include the details of what failed - * @param \DutchCodingCompany\LaravelJsonSchema\Contracts\JsonSchemaValidator|null $schemaValidator custom repository, otherwise resolved from the service container */ public function __construct( protected string $schema, protected bool $detailedMessage = true, JsonSchemaValidator | null $schemaValidator = null, ) { - $this->schemaValidator = $schemaValidator ?? Container::getInstance()->make(JsonSchemaValidator::class); + parent::__construct($detailedMessage, $schemaValidator); } - /** - * Run the validation rule. - */ - public function validate(string $attribute, mixed $value, Closure $fail): void + protected function determineSchemaName(): string { - $result = $this->schemaValidator->validate($this->schema, $value); - - if ($result->failed()) { - $fail($this->detailedMessage - ? 'json-schema::messages.detailed-error-message' - : 'json-schema::messages.error-message' - )->translate([ - 'attribute' => $attribute, - 'details' => $result->getMessage() ?? '', - ]); - } + return $this->schema; } }