Skip to content

Commit

Permalink
merge rule type customization
Browse files Browse the repository at this point in the history
  • Loading branch information
Riley Aven committed Aug 30, 2024
1 parent 4835b0b commit 4475691
Show file tree
Hide file tree
Showing 13 changed files with 232 additions and 102 deletions.
34 changes: 4 additions & 30 deletions config/rules-to-schema.php
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
<?php

use LaravelRulesToSchema\LaravelRuleType;
use LaravelRulesToSchema\Parsers\ConfirmedParser;
use LaravelRulesToSchema\Parsers\CustomRuleSchemaParser;
use LaravelRulesToSchema\Parsers\EnumParser;
use LaravelRulesToSchema\Parsers\ExcludedParser;
use LaravelRulesToSchema\Parsers\FormatParser;
use LaravelRulesToSchema\Parsers\MiscPropertyParser;
use LaravelRulesToSchema\Parsers\NestedObjectParser;
use LaravelRulesToSchema\Parsers\RequiredParser;
use LaravelRulesToSchema\Parsers\RuleHasJsonSchemaParser;
use LaravelRulesToSchema\Parsers\TypeParser;

return [
Expand All @@ -32,40 +31,15 @@
EnumParser::class,
ExcludedParser::class,
ConfirmedParser::class,
RuleHasJsonSchemaParser::class,
],

/*
* For convenience, simple types for custom rules can be added here
*/
'rule_type_map' => [
'string' => [
...LaravelRuleType::string(),
],
'integer' => [
...LaravelRuleType::integer(),
],
'number' => [
...LaravelRuleType::number(),
],
'boolean' => [
...LaravelRuleType::boolean(),
],
'nullable' => [
...LaravelRuleType::nullable(),
],
'array' => [
...LaravelRuleType::array(),
],
'exclude' => [
...LaravelRuleType::exclude(),
],
CustomRuleSchemaParser::class,
],

/*
* Third party rules that you can provide custom schema definitions for
*/
'custom_rule_schemas' => [
// \CustomPackage\CustomRule::class => \Support\CustomRuleSchemaDefinition::class,
// \CustomPackage\CustomRule::class => 'string',
// \CustomPackage\CustomRule::class => ['null', 'string'],
],
];
2 changes: 2 additions & 0 deletions src/Facades/LaravelRulesToSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

/**
* @method static FluentSchema parse(array $rules) Parse Rules
* @method static void registerParser(string $parser) Register a rule parser
* @method static void registerCustomRuleSchema(string $rule, mixed $type) Register a schema for a custom rule
*
* @see \LaravelRulesToSchema\LaravelRulesToSchema
*/
Expand Down
31 changes: 31 additions & 0 deletions src/LaravelRulesToSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ class LaravelRulesToSchema
{
use ParsesNormalizedRuleset;

protected static array $additionalParsers = [];

protected static array $additionalCustomSchemas = [];

public function parse(array $rules): FluentSchema
{
$normalizedRules = (new ValidationRuleNormalizer($rules))->getRules();
Expand All @@ -28,4 +32,31 @@ public function parse(array $rules): FluentSchema

return $schema;
}

public function getParsers(): array
{
return array_merge(
config('rules-to-schema.parsers'),
self::$additionalParsers,
);
}

public function registerParser(string $parser): void
{
self::$additionalParsers[] = $parser;
}

public function getCustomRuleSchemas(): array
{

return array_merge(
config('rules-to-schema.custom_rule_schemas'),
self::$additionalCustomSchemas,
);
}

public function registerCustomRuleSchema(string $rule, mixed $type): void
{
self::$additionalCustomSchemas[$rule] = $type;
}
}
57 changes: 57 additions & 0 deletions src/Parsers/CustomRuleSchemaParser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

namespace LaravelRulesToSchema\Parsers;

use FluentJsonSchema\Enums\JsonSchemaType;
use FluentJsonSchema\FluentSchema;
use LaravelRulesToSchema\Contracts\HasJsonSchema;
use LaravelRulesToSchema\Contracts\RuleParser;
use LaravelRulesToSchema\Facades\LaravelRulesToSchema;
use PHPUnit\Logging\Exception;

class CustomRuleSchemaParser implements RuleParser
{
/**
* {@inheritDoc}
*/
public function __invoke(string $attribute, FluentSchema $schema, array $validationRules, array $nestedRuleset): array|FluentSchema|null
{
foreach ($validationRules as $ruleArgs) {
[$rule, $args] = $ruleArgs;

$ruleName = is_object($rule) ? get_class($rule) : $rule;

if ($rule instanceof HasJsonSchema) {
return $rule->toJsonSchema($attribute);
} elseif (array_key_exists($ruleName, LaravelRulesToSchema::getCustomRuleSchemas())) {
$typehint = LaravelRulesToSchema::getCustomRuleSchemas()[$ruleName];

if (is_string($typehint)) {
if (class_exists($typehint)) {
$instance = app($typehint);

if (! $instance instanceof HasJsonSchema) {
throw new Exception('Custom rule schemas must implement '.HasJsonSchema::class);
}

return $instance->toJsonSchema($attribute);
} else {
$schema->type()->fromString($typehint);
}
} elseif ($typehint instanceof JsonSchemaType) {
$schema->type()->fromString($typehint->value);
} elseif (is_array($typehint)) {
foreach ($typehint as $type) {
if ($type instanceof JsonSchemaType) {
$schema->type()->fromString($type->value);
} else {
$schema->type()->fromString($type);
}
}
}
}
}

return $schema;
}
}
3 changes: 2 additions & 1 deletion src/Parsers/ExcludedParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@

use FluentJsonSchema\FluentSchema;
use LaravelRulesToSchema\Contracts\RuleParser;
use LaravelRulesToSchema\LaravelRuleType;

class ExcludedParser implements RuleParser
{
public function __invoke(string $attribute, FluentSchema $schema, array $validationRules, array $nestedRuleset): array|FluentSchema|null
{
foreach ($validationRules as $ruleArgs) {
[$rule, $args] = $ruleArgs;
if (is_string($rule) && in_array($rule, config('rules-to-schema.rule_type_map.exclude'))) {
if (is_string($rule) && in_array($rule, LaravelRuleType::exclude())) {
return null;
}
}
Expand Down
39 changes: 0 additions & 39 deletions src/Parsers/RuleHasJsonSchemaParser.php

This file was deleted.

13 changes: 7 additions & 6 deletions src/Parsers/TypeParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Illuminate\Validation\Rules\Enum as EnumRule;
use Illuminate\Validation\Rules\In as InRule;
use LaravelRulesToSchema\Contracts\RuleParser;
use LaravelRulesToSchema\LaravelRuleType;
use ReflectionClass;

class TypeParser implements RuleParser
Expand All @@ -17,25 +18,25 @@ public function __invoke(string $attribute, FluentSchema $schema, array $validat

$ruleName = is_object($rule) ? get_class($rule) : $rule;

if (in_array($ruleName, config('rules-to-schema.rule_type_map.string'))) {
if (in_array($ruleName, LaravelRuleType::string())) {
$schema->type()->string();
}
if (in_array($ruleName, config('rules-to-schema.rule_type_map.integer'))) {
if (in_array($ruleName, LaravelRuleType::integer())) {
$schema->type()->integer();
}
if (in_array($ruleName, config('rules-to-schema.rule_type_map.number'))) {
if (in_array($ruleName, LaravelRuleType::number())) {
$schema->type()->number();
}
if (in_array($ruleName, config('rules-to-schema.rule_type_map.boolean'))) {
if (in_array($ruleName, LaravelRuleType::boolean())) {
$schema->type()->boolean();
}
if (in_array($ruleName, config('rules-to-schema.rule_type_map.array'))) {
if (in_array($ruleName, LaravelRuleType::array())) {
// Check if what we are dealing with is not an object type with properties
if (count(array_diff_key($nestedRuleset, array_flip([config('rules-to-schema.validation_rule_token')]))) == 0) {
$schema->type()->array();
}
}
if (in_array($ruleName, config('rules-to-schema.rule_type_map.nullable'))) {
if (in_array($ruleName, LaravelRuleType::nullable())) {
$schema->type()->null();
}

Expand Down
7 changes: 4 additions & 3 deletions src/ParsesNormalizedRuleset.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace LaravelRulesToSchema;

use FluentJsonSchema\FluentSchema;
use LaravelRulesToSchema\Contracts\RuleParser;
use Mockery\Exception;

trait ParsesNormalizedRuleset
Expand All @@ -13,11 +14,11 @@ public function parseRuleset(string $name, array $nestedRuleset): null|FluentSch

$schemas = [$name => FluentSchema::make()];

foreach (config('rules-to-schema.parsers') as $parserClass) {
foreach (\LaravelRulesToSchema\Facades\LaravelRulesToSchema::getParsers() as $parserClass) {
$instance = app($parserClass);

if (! $instance instanceof \LaravelRulesToSchema\Contracts\RuleParser) {
throw new Exception('Rule parsers must implement '.\LaravelRulesToSchema\Contracts\RuleParser::class);
if (! $instance instanceof RuleParser) {
throw new Exception('Rule parsers must implement '.RuleParser::class);
}

$newSchemas = [];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"type": "object",
"properties": {
"custom": {
"type": "array",
"items": {
"type": "integer"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"type": "object",
"properties": {
"custom": {
"type": [
"null",
"string"
]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,36 @@
}
}
}
},
"integer": {
"type": "integer"
},
"number": {
"type": "number"
},
"boolean": {
"type": "boolean"
},
"array": {
"type": "array"
},
"nullable": {
"type": "null"
},
"custom_rule_as_enum": {
"type": "string"
},
"custom_rule_as_enum_array": {
"type": [
"null",
"number"
]
},
"custom_multiple_types": {
"type": [
"null",
"string"
]
}
}
}
Loading

0 comments on commit 4475691

Please sign in to comment.