Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
8 changes: 8 additions & 0 deletions src/Contracts/JsonSchemaNameProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace DutchCodingCompany\LaravelJsonSchema\Contracts;

interface JsonSchemaNameProvider
{
public function getSchemaName(): string;
}
44 changes: 44 additions & 0 deletions src/Rules/BaseRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

namespace DutchCodingCompany\LaravelJsonSchema\Rules;

use Closure;
use DutchCodingCompany\LaravelJsonSchema\Contracts\JsonSchemaValidator;
use Illuminate\Container\Container;
use Illuminate\Contracts\Validation\ValidationRule;

abstract class BaseRule implements ValidationRule
{
protected JsonSchemaValidator $schemaValidator;

/**
* @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 bool $detailedMessage = true,
JsonSchemaValidator | null $schemaValidator = null,
) {
$this->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;
}
62 changes: 62 additions & 0 deletions src/Rules/JsonSchemaAttributeRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

namespace DutchCodingCompany\LaravelJsonSchema\Rules;

use DutchCodingCompany\LaravelJsonSchema\Contracts\JsonSchemaNameProvider;
use DutchCodingCompany\LaravelJsonSchema\Contracts\JsonSchemaValidator;
use Illuminate\Contracts\Validation\DataAwareRule;
use InvalidArgumentException;

class JsonSchemaAttributeRule extends BaseRule implements DataAwareRule
{
/**
* All of the data under validation.
*
* @var array<string, mixed>
*/
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<string, mixed> $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.'"');
}
}
27 changes: 4 additions & 23 deletions src/Rules/JsonSchemaRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}