refactor
This commit is contained in:
parent
367858c97c
commit
6b1a385292
13 changed files with 191 additions and 80 deletions
|
|
@ -4,7 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Icefox\DTO;
|
||||
|
||||
use Icefox\DTO\Support\RuleFactory;
|
||||
use Icefox\DTO\RuleFactory;
|
||||
use Illuminate\Support\Collection;
|
||||
use phpDocumentor\Reflection\PseudoTypes\Generic;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Icefox\DTO\Factories;
|
||||
namespace Icefox\DTO;
|
||||
|
||||
use Icefox\DTO\ParameterMeta;
|
||||
use Icefox\DTO\Support\RuleFactory;
|
||||
use Icefox\DTO\RuleFactory;
|
||||
use phpDocumentor\Reflection\PseudoTypes\Generic;
|
||||
|
||||
class CollectionFactory
|
||||
class CustomHandlers
|
||||
{
|
||||
/**
|
||||
* @return array<string,string[]>
|
||||
*/
|
||||
public static function rules(ParameterMeta $parameter, RuleFactory $factory): array
|
||||
public static function CollectionRules(ParameterMeta $parameter, RuleFactory $factory): array
|
||||
{
|
||||
if (is_null($parameter->tag)) {
|
||||
return [];
|
||||
|
|
@ -36,3 +35,4 @@ class CollectionFactory
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4,14 +4,14 @@ namespace Icefox\DTO;
|
|||
|
||||
use Icefox\DTO\Attributes\FromInput;
|
||||
use Icefox\DTO\Attributes\FromRouteParameter;
|
||||
use Icefox\DTO\Support\RuleFactory;
|
||||
use Icefox\DTO\Support\ValueFactory;
|
||||
use Icefox\DTO\RuleFactory;
|
||||
use Icefox\DTO\ValueFactory;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Route;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Illuminate\Validation\Validator;
|
||||
use ReflectionClass;
|
||||
|
||||
class DataObjectFactory
|
||||
{
|
||||
|
|
@ -31,7 +31,7 @@ class DataObjectFactory
|
|||
*/
|
||||
public static function fromArray(string $class, array $input, array $routeParameters): ?object
|
||||
{
|
||||
$logger = new Log();
|
||||
$logger = Log::channel('dto');
|
||||
$parameters = ReflectionHelper::getParametersMeta($class);
|
||||
foreach ($parameters as $parameter) {
|
||||
$parameterName = $parameter->reflection->getName();
|
||||
|
|
@ -60,7 +60,7 @@ class DataObjectFactory
|
|||
// continue;
|
||||
// }
|
||||
}
|
||||
$logger->inputRaw($input);
|
||||
$logger->debug('input', $input);
|
||||
|
||||
$rules = (new RuleFactory($logger))->make($class);
|
||||
|
||||
|
|
@ -69,7 +69,7 @@ class DataObjectFactory
|
|||
: App::makeWith(Validator::class, ['data' => $input, 'rules' => $rules]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
$logger->validationErrors($validator->errors()->toArray());
|
||||
$logger->warning('validation error', $validator->errors()->toArray());
|
||||
if (method_exists($class, 'fails')) {
|
||||
return App::call("$class::fails", ['validator' => $validator ]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Icefox\DTO;
|
||||
|
||||
use Icefox\DTO\Attributes\FromInput;
|
||||
use Icefox\DTO\Attributes\FromRouteParameter;
|
||||
|
||||
class InputFactory
|
||||
{
|
||||
public function __construct(public readonly Log $log) {}
|
||||
|
||||
public function make(string $class): array
|
||||
{
|
||||
$map = [];
|
||||
$parameters = ReflectionHelper::getParametersMeta($class);
|
||||
foreach ($parameters as $parameter) {
|
||||
$name = $parameter->reflection->getName();
|
||||
|
||||
foreach ($parameter->reflection->getAttributes(FromRouteParameter::class) as $attr) {
|
||||
$map[$name][] = 'route_' . $attr->newInstance()->name;
|
||||
}
|
||||
|
||||
foreach ($parameter->reflection->getAttributes(FromInput::class) as $attr) {
|
||||
$map[$name][] = $attr->newInstance()->name;
|
||||
}
|
||||
|
||||
$map[$name][] = $name;
|
||||
}
|
||||
return $map;
|
||||
}
|
||||
|
||||
private static self $_instance;
|
||||
|
||||
public static function instance(): self
|
||||
{
|
||||
if (empty(self::$_instance)) {
|
||||
self::$_instance = new self(new Log());
|
||||
}
|
||||
return self::$_instance;
|
||||
}
|
||||
}
|
||||
|
|
@ -45,4 +45,3 @@ class ReflectionHelper
|
|||
return self::$cache[$class];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,13 +2,14 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Icefox\DTO\Support;
|
||||
namespace Icefox\DTO;
|
||||
|
||||
use Icefox\DTO\Attributes\Flat;
|
||||
use Icefox\DTO\Attributes\OverwriteRules;
|
||||
use Icefox\DTO\Config;
|
||||
use Icefox\DTO\ParameterMeta;
|
||||
use Icefox\DTO\ReflectionHelper;
|
||||
use Icefox\DTO\RuleFactory;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
|
@ -2,16 +2,14 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Icefox\DTO\Support;
|
||||
namespace Icefox\DTO;
|
||||
|
||||
use Icefox\DTO\Attributes\CastWith;
|
||||
use Icefox\DTO\Attributes\Flat;
|
||||
use Icefox\DTO\Config;
|
||||
use Icefox\DTO\ParameterMeta;
|
||||
use Icefox\DTO\ReflectionHelper;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use ReflectionNamedType;
|
||||
use ReflectionParameter;
|
||||
use phpDocumentor\Reflection\DocBlock\Tags\Param;
|
||||
use phpDocumentor\Reflection\PseudoTypes\Generic;
|
||||
use phpDocumentor\Reflection\Type;
|
||||
|
|
@ -102,6 +100,9 @@ class ValueFactory
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $input
|
||||
*/
|
||||
public static function make(string $class, array $input): object
|
||||
{
|
||||
$parameters = ReflectionHelper::getParametersMeta($class);
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
namespace Tests;
|
||||
|
||||
use Icefox\DTO\Log;
|
||||
use Icefox\DTO\Support\RuleFactory;
|
||||
use Icefox\DTO\RuleFactory;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Tests\Classes\ArrayDataObject;
|
||||
use Tests\Classes\CollectionDataObject;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ namespace Tests\Rules;
|
|||
|
||||
use Icefox\DTO\Log;
|
||||
use Icefox\DTO\ReflectionHelper;
|
||||
use Icefox\DTO\Support\RuleFactory;
|
||||
use Icefox\DTO\RuleFactory;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Tests\Rules\WithEmptyOverwriteRules;
|
||||
use Tests\Rules\WithMergedRules;
|
||||
|
|
|
|||
|
|
@ -5,10 +5,8 @@ declare(strict_types=1);
|
|||
namespace Tests;
|
||||
|
||||
use Icefox\DTO\Attributes\Flat;
|
||||
use Icefox\DTO\Support\RuleFactory;
|
||||
use Icefox\DTO\RuleFactory;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
readonly class BasicPrimitives
|
||||
{
|
||||
|
|
|
|||
165
tests/ValuesTest.php
Normal file
165
tests/ValuesTest.php
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests;
|
||||
|
||||
use Icefox\DTO\Attributes\Flat;
|
||||
use Icefox\DTO\RuleFactory;
|
||||
use Icefox\DTO\ValueFactory;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
readonly class BasicPrimitives
|
||||
{
|
||||
public function __construct(
|
||||
public string $text,
|
||||
public int $number,
|
||||
public bool $flag,
|
||||
public ?array $items,
|
||||
public float $floating = 0.0,
|
||||
) {}
|
||||
}
|
||||
|
||||
test('required rules', function () {
|
||||
$object = ValueFactory::make(BasicPrimitives::class, [
|
||||
'text' => 'abc',
|
||||
'number' => 42,
|
||||
'flag' => false,
|
||||
'items' => ['a', 2, true],
|
||||
'floating' => 4.2,
|
||||
]);
|
||||
expect($object->text)->toBe('abc');
|
||||
expect($object->number)->toBe(42);
|
||||
expect($object->flag)->toBe(false);
|
||||
expect($object->items)->toBe(['a', 2, true]);
|
||||
expect($object->floating)->toEqualWithDelta(4.2, 0.000001);
|
||||
});
|
||||
|
||||
readonly class AnnotatedArray
|
||||
{
|
||||
/**
|
||||
* @param array<int,float> $items
|
||||
*/
|
||||
public function __construct(public array $items) {}
|
||||
}
|
||||
test('annotated array', function () {
|
||||
expect(RuleFactory::instance()->make(AnnotatedArray::class))->toBe([
|
||||
'items' => ['required', 'array'],
|
||||
'items.*' => ['required', 'numeric'],
|
||||
]);
|
||||
});
|
||||
|
||||
readonly class AnnotatedArrayNullableValue
|
||||
{
|
||||
/**
|
||||
* @param array<?float> $items
|
||||
*/
|
||||
public function __construct(public array $items) {}
|
||||
}
|
||||
test('annotated array with nullable items', function () {
|
||||
expect(RuleFactory::instance()->make(AnnotatedArrayNullableValue::class))->toBe([
|
||||
'items' => ['required', 'array'],
|
||||
'items.*' => ['nullable', 'numeric'],
|
||||
]);
|
||||
});
|
||||
|
||||
readonly class PlainLeaf
|
||||
{
|
||||
public function __construct(public string $name) {}
|
||||
}
|
||||
readonly class PlainRoot
|
||||
{
|
||||
public function __construct(public int $value, public PlainLeaf $leaf) {}
|
||||
}
|
||||
|
||||
test('plain nesting', function () {
|
||||
expect(RuleFactory::instance()->make(PlainRoot::class))->toBe([
|
||||
'value' => ['required', 'numeric'],
|
||||
'leaf' => ['required'],
|
||||
'leaf.name' => ['required'],
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
readonly class AnnotatedArrayItem
|
||||
{
|
||||
public function __construct(public int $value) {}
|
||||
}
|
||||
|
||||
readonly class AnnotatedArrayObject
|
||||
{
|
||||
/**
|
||||
* @param ?array<AnnotatedArrayItem> $items
|
||||
*/
|
||||
public function __construct(public ?array $items) {}
|
||||
}
|
||||
|
||||
test('annotated array with object', function () {
|
||||
expect(RuleFactory::instance()->make(AnnotatedArrayObject::class))->toBe([
|
||||
'items' => ['nullable', 'array'],
|
||||
'items.*' => ['required'],
|
||||
'items.*.value' => ['required', 'numeric'],
|
||||
]);
|
||||
});
|
||||
|
||||
readonly class FlattenedLeaf
|
||||
{
|
||||
public function __construct(public ?bool $flag) {}
|
||||
}
|
||||
|
||||
readonly class NotFlattenedLeaf
|
||||
{
|
||||
public function __construct(public string $description) {}
|
||||
}
|
||||
|
||||
readonly class FlattenedNode
|
||||
{
|
||||
public function __construct(
|
||||
public string $id,
|
||||
public NotFlattenedLeaf $leaf,
|
||||
#[Flat]
|
||||
public FlattenedLeaf $squish,
|
||||
public int $level = 1,
|
||||
) {}
|
||||
}
|
||||
|
||||
readonly class FlattenedRoot
|
||||
{
|
||||
public function __construct(
|
||||
public int $value,
|
||||
#[Flat]
|
||||
public FlattenedNode $node,
|
||||
) {}
|
||||
}
|
||||
|
||||
test('flattened basic', function () {
|
||||
expect(RuleFactory::instance()->make(FlattenedRoot::class))->toBe([
|
||||
'value' => ['required', 'numeric'],
|
||||
'id' => ['required' ],
|
||||
'leaf' => ['required'],
|
||||
'leaf.description' => ['required'],
|
||||
'flag' => ['nullable', 'boolean'],
|
||||
'level' => ['sometimes', 'numeric'],
|
||||
]);
|
||||
});
|
||||
|
||||
readonly class AnnotatedCollectionItem
|
||||
{
|
||||
public function __construct(public int $value) {}
|
||||
}
|
||||
|
||||
readonly class AnnotatedCollection
|
||||
{
|
||||
/**
|
||||
* @param Collection<AnnotatedCollectionItem> $group
|
||||
*/
|
||||
public function __construct(public Collection $group) {}
|
||||
}
|
||||
|
||||
test('annotated collection', function () {
|
||||
expect(RuleFactory::instance()->make(AnnotatedCollection::class))->toBe([
|
||||
'group' => ['required', 'array'],
|
||||
'group.*' => ['required'],
|
||||
'group.*.value' => ['required', 'numeric'],
|
||||
]);
|
||||
});
|
||||
|
|
@ -1,8 +1,13 @@
|
|||
<?php
|
||||
|
||||
use Icefox\DTO\CustomHandlers;
|
||||
use Illuminate\Support\Collection;
|
||||
use Psr\Log\LogLevel;
|
||||
|
||||
return [
|
||||
'rules' => [
|
||||
Collection::class => CustomHandlers::CollectionRules(...),
|
||||
],
|
||||
'logging' => [
|
||||
'channel' => 'dto',
|
||||
'context' => [
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="root">
|
||||
<directory>tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<source>
|
||||
<include>
|
||||
<directory>app</directory>
|
||||
</include>
|
||||
</source>
|
||||
</phpunit>
|
||||
Loading…
Add table
Add a link
Reference in a new issue