diff --git a/src/Support/RuleFactory.php b/src/Support/RuleFactory.php index bd48324..05e4557 100644 --- a/src/Support/RuleFactory.php +++ b/src/Support/RuleFactory.php @@ -5,9 +5,9 @@ declare(strict_types=1); namespace Icefox\DTO\Support; use Icefox\DTO\Attributes\CastWith; +use Icefox\DTO\Attributes\Flat; use Icefox\DTO\Attributes\OverwriteRules; use Icefox\DTO\Config; -use Icefox\DTO\Attributes\FromMapper; use Icefox\DTO\Log; use Icefox\DTO\ParameterMeta; use Icefox\DTO\ReflectionHelper; @@ -15,16 +15,12 @@ use Illuminate\Support\Facades\App; use Illuminate\Validation\Rule; use ReflectionClass; use ReflectionNamedType; -use ReflectionParameter; use ReflectionUnionType; use BackedEnum; -use phpDocumentor\Reflection\DocBlockFactory; -use phpDocumentor\Reflection\DocBlock\Tag; use phpDocumentor\Reflection\DocBlock\Tags\Param; use phpDocumentor\Reflection\Type; use phpDocumentor\Reflection\Types\AbstractList; use phpDocumentor\Reflection\Types\Boolean; -use phpDocumentor\Reflection\Types\ContextFactory; use phpDocumentor\Reflection\Types\Float_; use phpDocumentor\Reflection\Types\Integer; use phpDocumentor\Reflection\Types\Nullable; @@ -61,7 +57,7 @@ class RuleFactory $paramsSub = ReflectionHelper::getParametersMeta($type->getFqsen()->__toString()); $rules = array_merge( $rules, - self::infer($paramsSub, $prefix . '.'), + self::infer($paramsSub, $prefix), ); } return $rules; @@ -71,10 +67,13 @@ class RuleFactory * @param array $parameters * @return array> */ - public static function infer(array $parameters, string $prefix): array + public static function infer(array $parameters, string $basePrefix): array { $rules = []; foreach ($parameters as $parameter) { + $prefix = $basePrefix + . (empty($basePrefix) ? '' : '.') + . (empty($parameter->reflection->getAttributes(Flat::class)) ? $parameter->reflection->getName() : ''); foreach (self::buildParameterRule($parameter, $prefix) as $key => $newRules) { $rules[$key] = $newRules; } @@ -89,18 +88,17 @@ class RuleFactory { $type = $parameter->reflection->getType(); - $root = $prefix . $parameter->reflection->getName(); if (empty($type)) { - return [$root => $parameter->reflection->isOptional() ? ['sometimes'] : ['required']]; + return [$prefix => $parameter->reflection->isOptional() ? ['sometimes'] : ['required']]; } - $rules = [$root => []]; + $rules = [$prefix => []]; if ($parameter->reflection->isOptional()) { - $rules[$root][] = 'sometimes'; + $rules[$prefix][] = 'sometimes'; } elseif ($type->allowsNull()) { - $rules[$root][] = 'nullable'; + $rules[$prefix][] = 'nullable'; } else { - $rules[$root][] = 'required'; + $rules[$prefix][] = 'required'; } if ($type instanceof ReflectionUnionType) { @@ -111,27 +109,27 @@ class RuleFactory if ($type instanceof ReflectionNamedType && $name = $type->getName()) { if ($globalRules = Config::getRules($name)) { foreach ($globalRules($parameter->reflection, $parameter->tag->getType()) as $scopedPrefix => $values) { - $realPrefix = $root . $scopedPrefix; + $realPrefix = $prefix . $scopedPrefix; $rules[$realPrefix] = array_values(array_unique(array_merge($rules[$realPrefix] ?? [], $values))); } } if ($name === 'string') { } elseif ($name === 'bool') { - $rules[$root][] = 'boolean'; + $rules[$prefix][] = 'boolean'; } elseif ($name === 'int' || $name === 'float') { - $rules[$root][] = 'numeric'; + $rules[$prefix][] = 'numeric'; } elseif ($name === 'array') { - $rules[$root][] = 'array'; + $rules[$prefix][] = 'array'; } elseif (enum_exists($name)) { $ref = new ReflectionClass($name); if ($ref->isSubclassOf(BackedEnum::class)) { - $rules[$root][] = Rule::enum($name); + $rules[$prefix][] = Rule::enum($name); } } else { $paramsSub = ReflectionHelper::getParametersMeta($type->getName()); $rules = array_merge( $rules, - self::infer($paramsSub, $root . '.'), + self::infer($paramsSub, $prefix), ); } } @@ -141,15 +139,16 @@ class RuleFactory if (method_exists($mapperClass, 'rules')) { $subRules = App::call("$mapperClass@rules"); foreach ($subRules as $key => &$value) { - $path = empty($key) ? $root : ($root . '.' . $key); + $path = empty($key) ? $prefix : ($prefix . '.' . $key); $rules[$path] = array_values(array_unique(array_merge($rules[$path] ?? [], $value))); } } } + if ($parameter->tag instanceof Param) { $docblockRules = self::getRulesFromDocBlock( $parameter->tag->getType(), - $prefix . $parameter->reflection->getName(), + $prefix, ); foreach ($docblockRules as $key => &$values) { $rules[$key] = array_values(array_unique(array_merge($rules[$key] ?? [], $values))); diff --git a/tests/Flattening/Classes/BasicRoot.php b/tests/Flattening/Classes/BasicRoot.php new file mode 100644 index 0000000..299f858 --- /dev/null +++ b/tests/Flattening/Classes/BasicRoot.php @@ -0,0 +1,12 @@ +make(BasicRoot::class); + expect($rules)->toMatchArray([ + 'text' => ['required'], + 'value' => ['required', 'numeric'], + ]); + }); +}); diff --git a/tests/Rules/RulesTest.php b/tests/Rules/RulesTest.php index c769acd..fca9610 100644 --- a/tests/Rules/RulesTest.php +++ b/tests/Rules/RulesTest.php @@ -15,7 +15,7 @@ use Tests\Rules\WithOverwriteRules; describe('rules array shape', function () { it('returns inferred rules shape from RuleFactory::infer (inferred only)', function () { $parameters = ReflectionHelper::getParametersMeta(WithMergedRules::class); - $rules = RuleFactory::infer($parameters, ''); + $rules = RuleFactory::infer($parameters, '', ''); expect($rules)->toBe([ 'value' => ['required', 'numeric'], @@ -24,7 +24,7 @@ describe('rules array shape', function () { it('returns inferred rules shape regardless of OverwriteRules attribute', function () { $parameters = ReflectionHelper::getParametersMeta(WithOverwriteRules::class); - $rules = RuleFactory::infer($parameters, ''); + $rules = RuleFactory::infer($parameters, '', ''); expect($rules)->toBe([ 'value' => ['required', 'numeric'],