wip flattening
This commit is contained in:
parent
74f151df07
commit
d83a324eb0
5 changed files with 64 additions and 23 deletions
|
|
@ -5,9 +5,9 @@ declare(strict_types=1);
|
||||||
namespace Icefox\DTO\Support;
|
namespace Icefox\DTO\Support;
|
||||||
|
|
||||||
use Icefox\DTO\Attributes\CastWith;
|
use Icefox\DTO\Attributes\CastWith;
|
||||||
|
use Icefox\DTO\Attributes\Flat;
|
||||||
use Icefox\DTO\Attributes\OverwriteRules;
|
use Icefox\DTO\Attributes\OverwriteRules;
|
||||||
use Icefox\DTO\Config;
|
use Icefox\DTO\Config;
|
||||||
use Icefox\DTO\Attributes\FromMapper;
|
|
||||||
use Icefox\DTO\Log;
|
use Icefox\DTO\Log;
|
||||||
use Icefox\DTO\ParameterMeta;
|
use Icefox\DTO\ParameterMeta;
|
||||||
use Icefox\DTO\ReflectionHelper;
|
use Icefox\DTO\ReflectionHelper;
|
||||||
|
|
@ -15,16 +15,12 @@ use Illuminate\Support\Facades\App;
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
use ReflectionClass;
|
use ReflectionClass;
|
||||||
use ReflectionNamedType;
|
use ReflectionNamedType;
|
||||||
use ReflectionParameter;
|
|
||||||
use ReflectionUnionType;
|
use ReflectionUnionType;
|
||||||
use BackedEnum;
|
use BackedEnum;
|
||||||
use phpDocumentor\Reflection\DocBlockFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Param;
|
use phpDocumentor\Reflection\DocBlock\Tags\Param;
|
||||||
use phpDocumentor\Reflection\Type;
|
use phpDocumentor\Reflection\Type;
|
||||||
use phpDocumentor\Reflection\Types\AbstractList;
|
use phpDocumentor\Reflection\Types\AbstractList;
|
||||||
use phpDocumentor\Reflection\Types\Boolean;
|
use phpDocumentor\Reflection\Types\Boolean;
|
||||||
use phpDocumentor\Reflection\Types\ContextFactory;
|
|
||||||
use phpDocumentor\Reflection\Types\Float_;
|
use phpDocumentor\Reflection\Types\Float_;
|
||||||
use phpDocumentor\Reflection\Types\Integer;
|
use phpDocumentor\Reflection\Types\Integer;
|
||||||
use phpDocumentor\Reflection\Types\Nullable;
|
use phpDocumentor\Reflection\Types\Nullable;
|
||||||
|
|
@ -61,7 +57,7 @@ class RuleFactory
|
||||||
$paramsSub = ReflectionHelper::getParametersMeta($type->getFqsen()->__toString());
|
$paramsSub = ReflectionHelper::getParametersMeta($type->getFqsen()->__toString());
|
||||||
$rules = array_merge(
|
$rules = array_merge(
|
||||||
$rules,
|
$rules,
|
||||||
self::infer($paramsSub, $prefix . '.'),
|
self::infer($paramsSub, $prefix),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return $rules;
|
return $rules;
|
||||||
|
|
@ -71,10 +67,13 @@ class RuleFactory
|
||||||
* @param array<ParameterMeta> $parameters
|
* @param array<ParameterMeta> $parameters
|
||||||
* @return array<string,array<int,string|Rule>>
|
* @return array<string,array<int,string|Rule>>
|
||||||
*/
|
*/
|
||||||
public static function infer(array $parameters, string $prefix): array
|
public static function infer(array $parameters, string $basePrefix): array
|
||||||
{
|
{
|
||||||
$rules = [];
|
$rules = [];
|
||||||
foreach ($parameters as $parameter) {
|
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) {
|
foreach (self::buildParameterRule($parameter, $prefix) as $key => $newRules) {
|
||||||
$rules[$key] = $newRules;
|
$rules[$key] = $newRules;
|
||||||
}
|
}
|
||||||
|
|
@ -89,18 +88,17 @@ class RuleFactory
|
||||||
{
|
{
|
||||||
$type = $parameter->reflection->getType();
|
$type = $parameter->reflection->getType();
|
||||||
|
|
||||||
$root = $prefix . $parameter->reflection->getName();
|
|
||||||
if (empty($type)) {
|
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()) {
|
if ($parameter->reflection->isOptional()) {
|
||||||
$rules[$root][] = 'sometimes';
|
$rules[$prefix][] = 'sometimes';
|
||||||
} elseif ($type->allowsNull()) {
|
} elseif ($type->allowsNull()) {
|
||||||
$rules[$root][] = 'nullable';
|
$rules[$prefix][] = 'nullable';
|
||||||
} else {
|
} else {
|
||||||
$rules[$root][] = 'required';
|
$rules[$prefix][] = 'required';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($type instanceof ReflectionUnionType) {
|
if ($type instanceof ReflectionUnionType) {
|
||||||
|
|
@ -111,27 +109,27 @@ class RuleFactory
|
||||||
if ($type instanceof ReflectionNamedType && $name = $type->getName()) {
|
if ($type instanceof ReflectionNamedType && $name = $type->getName()) {
|
||||||
if ($globalRules = Config::getRules($name)) {
|
if ($globalRules = Config::getRules($name)) {
|
||||||
foreach ($globalRules($parameter->reflection, $parameter->tag->getType()) as $scopedPrefix => $values) {
|
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)));
|
$rules[$realPrefix] = array_values(array_unique(array_merge($rules[$realPrefix] ?? [], $values)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($name === 'string') {
|
if ($name === 'string') {
|
||||||
} elseif ($name === 'bool') {
|
} elseif ($name === 'bool') {
|
||||||
$rules[$root][] = 'boolean';
|
$rules[$prefix][] = 'boolean';
|
||||||
} elseif ($name === 'int' || $name === 'float') {
|
} elseif ($name === 'int' || $name === 'float') {
|
||||||
$rules[$root][] = 'numeric';
|
$rules[$prefix][] = 'numeric';
|
||||||
} elseif ($name === 'array') {
|
} elseif ($name === 'array') {
|
||||||
$rules[$root][] = 'array';
|
$rules[$prefix][] = 'array';
|
||||||
} elseif (enum_exists($name)) {
|
} elseif (enum_exists($name)) {
|
||||||
$ref = new ReflectionClass($name);
|
$ref = new ReflectionClass($name);
|
||||||
if ($ref->isSubclassOf(BackedEnum::class)) {
|
if ($ref->isSubclassOf(BackedEnum::class)) {
|
||||||
$rules[$root][] = Rule::enum($name);
|
$rules[$prefix][] = Rule::enum($name);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$paramsSub = ReflectionHelper::getParametersMeta($type->getName());
|
$paramsSub = ReflectionHelper::getParametersMeta($type->getName());
|
||||||
$rules = array_merge(
|
$rules = array_merge(
|
||||||
$rules,
|
$rules,
|
||||||
self::infer($paramsSub, $root . '.'),
|
self::infer($paramsSub, $prefix),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -141,15 +139,16 @@ class RuleFactory
|
||||||
if (method_exists($mapperClass, 'rules')) {
|
if (method_exists($mapperClass, 'rules')) {
|
||||||
$subRules = App::call("$mapperClass@rules");
|
$subRules = App::call("$mapperClass@rules");
|
||||||
foreach ($subRules as $key => &$value) {
|
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)));
|
$rules[$path] = array_values(array_unique(array_merge($rules[$path] ?? [], $value)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($parameter->tag instanceof Param) {
|
if ($parameter->tag instanceof Param) {
|
||||||
$docblockRules = self::getRulesFromDocBlock(
|
$docblockRules = self::getRulesFromDocBlock(
|
||||||
$parameter->tag->getType(),
|
$parameter->tag->getType(),
|
||||||
$prefix . $parameter->reflection->getName(),
|
$prefix,
|
||||||
);
|
);
|
||||||
foreach ($docblockRules as $key => &$values) {
|
foreach ($docblockRules as $key => &$values) {
|
||||||
$rules[$key] = array_values(array_unique(array_merge($rules[$key] ?? [], $values)));
|
$rules[$key] = array_values(array_unique(array_merge($rules[$key] ?? [], $values)));
|
||||||
|
|
|
||||||
12
tests/Flattening/Classes/BasicRoot.php
Normal file
12
tests/Flattening/Classes/BasicRoot.php
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Flattening\Classes;
|
||||||
|
|
||||||
|
use Icefox\DTO\Attributes\Flat;
|
||||||
|
use Icefox\DTO\DataObject;
|
||||||
|
|
||||||
|
class BasicRoot
|
||||||
|
{
|
||||||
|
use DataObject;
|
||||||
|
public function __construct(public string $text, #[Flat] public RequiredLeaf $leaf) {}
|
||||||
|
}
|
||||||
12
tests/Flattening/Classes/RequiredLeaf.php
Normal file
12
tests/Flattening/Classes/RequiredLeaf.php
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Flattening\Classes;
|
||||||
|
|
||||||
|
use Icefox\DTO\DataObject;
|
||||||
|
|
||||||
|
class RequiredLeaf
|
||||||
|
{
|
||||||
|
use DataObject;
|
||||||
|
|
||||||
|
public function __construct(public int $value) {}
|
||||||
|
}
|
||||||
18
tests/Flattening/FlatteningTest.php
Normal file
18
tests/Flattening/FlatteningTest.php
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Flattening;
|
||||||
|
|
||||||
|
use Icefox\DTO\Log;
|
||||||
|
use Icefox\DTO\Support\RuleFactory;
|
||||||
|
use Tests\Flattening\Classes\BasicRoot;
|
||||||
|
|
||||||
|
describe('flattens required parameters', function () {
|
||||||
|
it('generates correct rules', function () {
|
||||||
|
|
||||||
|
$rules = (new RuleFactory(new Log()))->make(BasicRoot::class);
|
||||||
|
expect($rules)->toMatchArray([
|
||||||
|
'text' => ['required'],
|
||||||
|
'value' => ['required', 'numeric'],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -15,7 +15,7 @@ use Tests\Rules\WithOverwriteRules;
|
||||||
describe('rules array shape', function () {
|
describe('rules array shape', function () {
|
||||||
it('returns inferred rules shape from RuleFactory::infer (inferred only)', function () {
|
it('returns inferred rules shape from RuleFactory::infer (inferred only)', function () {
|
||||||
$parameters = ReflectionHelper::getParametersMeta(WithMergedRules::class);
|
$parameters = ReflectionHelper::getParametersMeta(WithMergedRules::class);
|
||||||
$rules = RuleFactory::infer($parameters, '');
|
$rules = RuleFactory::infer($parameters, '', '');
|
||||||
|
|
||||||
expect($rules)->toBe([
|
expect($rules)->toBe([
|
||||||
'value' => ['required', 'numeric'],
|
'value' => ['required', 'numeric'],
|
||||||
|
|
@ -24,7 +24,7 @@ describe('rules array shape', function () {
|
||||||
|
|
||||||
it('returns inferred rules shape regardless of OverwriteRules attribute', function () {
|
it('returns inferred rules shape regardless of OverwriteRules attribute', function () {
|
||||||
$parameters = ReflectionHelper::getParametersMeta(WithOverwriteRules::class);
|
$parameters = ReflectionHelper::getParametersMeta(WithOverwriteRules::class);
|
||||||
$rules = RuleFactory::infer($parameters, '');
|
$rules = RuleFactory::infer($parameters, '', '');
|
||||||
|
|
||||||
expect($rules)->toBe([
|
expect($rules)->toBe([
|
||||||
'value' => ['required', 'numeric'],
|
'value' => ['required', 'numeric'],
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue