diff --git a/src/Attributes/FromMapper.php b/src/Attributes/CastWith.php similarity index 93% rename from src/Attributes/FromMapper.php rename to src/Attributes/CastWith.php index ffdce99..6d544c5 100644 --- a/src/Attributes/FromMapper.php +++ b/src/Attributes/CastWith.php @@ -7,7 +7,7 @@ namespace Icefox\DTO\Attributes; use Attribute; #[Attribute(Attribute::TARGET_PARAMETER)] -class FromMapper +class CastWith { /** * @param class-string $class diff --git a/src/Config.php b/src/Config.php new file mode 100644 index 0000000..a25d8df --- /dev/null +++ b/src/Config.php @@ -0,0 +1,18 @@ +reflection->getName(); - if ($mapper = array_first($parameter->reflection->getAttributes(FromMapper::class))) { + if ($mapper = array_first($parameter->reflection->getAttributes(CastWith::class))) { $value = App::call( [App::make($mapper->newInstance()->class), 'map'], ['value' => $validator->getValue($parameterName)], diff --git a/src/Support/RuleFactory.php b/src/Support/RuleFactory.php index 623ebc9..a3572b6 100644 --- a/src/Support/RuleFactory.php +++ b/src/Support/RuleFactory.php @@ -4,6 +4,8 @@ declare(strict_types=1); namespace Icefox\DTO\Support; +use Icefox\DTO\Attributes\CastWith; +use Icefox\DTO\Config; use Icefox\DTO\Attributes\FromMapper; use Icefox\DTO\ParameterMeta; use Illuminate\Support\Facades\App; @@ -139,7 +141,7 @@ class RuleFactory } if ($type instanceof ReflectionNamedType && $name = $type->getName()) { - if ($globalRules = config('dto.rules.' . $name)) { + if ($globalRules = Config::getRules($name)) { foreach ($globalRules($parameter->reflection, $parameter->tag->getType()) as $scopedPrefix => $values) { $realPrefix = $root . $scopedPrefix; $rules[$realPrefix] = array_values(array_unique(array_merge($rules[$realPrefix] ?? [], $values))); @@ -166,7 +168,7 @@ class RuleFactory } } - foreach ($parameter->reflection->getAttributes(FromMapper::class) as $attr) { + foreach ($parameter->reflection->getAttributes(CastWith::class) as $attr) { $mapperClass = $attr->newInstance()->class; if (method_exists($mapperClass, 'rules')) { $subRules = App::call("$mapperClass@rules"); diff --git a/src/Support/ValueFactory.php b/src/Support/ValueFactory.php index efebe48..992402e 100644 --- a/src/Support/ValueFactory.php +++ b/src/Support/ValueFactory.php @@ -4,6 +4,8 @@ declare(strict_types=1); namespace Icefox\DTO\Support; +use Icefox\DTO\Attributes\CastWith; +use Icefox\DTO\Config; use Illuminate\Support\Facades\App; use ReflectionNamedType; use ReflectionParameter; @@ -20,7 +22,7 @@ class ValueFactory { public static function constructObject(string $className, mixed $rawValue): object { - if ($mapper = config('dto.mappers.' . $className, null)) { + if ($mapper = Config::getCaster($className)) { return $mapper($rawValue); } @@ -83,21 +85,32 @@ class ValueFactory return null; } - if (is_null($type)) { - $reflectedType = $reflection->getType(); - if ($reflectedType instanceof ReflectionNamedType && $name = $reflectedType->getName()) { - return match ($name) { - 'string' => $rawValue, - 'bool' => boolval($rawValue), - 'int' => intval($rawValue), - 'float' => floatval($rawValue), - 'array' => $rawValue, - default => self::constructObject($name, $rawValue), - }; - } - return $rawValue; + $castWithAttrs = $reflection->getAttributes(CastWith::class); + if ($withCast = $reflection->getAttributes(CastWith::class)[0] ?? null) { + $caster = $withCast->newInstance()->class; + return App::call("$caster@cast", ['value' => $rawValue]); } - return self::resolveTypedValue($rawValue, $type); + if (!is_null($type)) { + return self::resolveTypedValue($rawValue, $type); + } + + $reflectedType = $reflection->getType(); + if ($reflectedType instanceof ReflectionNamedType && $name = $reflectedType->getName()) { + if ($caster = Config::getCaster($name)) { + return App::call($caster, ['value' => $rawValue]); + } + + return match ($name) { + 'string' => $rawValue, + 'bool' => boolval($rawValue), + 'int' => intval($rawValue), + 'float' => floatval($rawValue), + 'array' => $rawValue, + default => self::constructObject($name, $rawValue), + }; + } + + return $rawValue; } } diff --git a/src/config/dto.php b/src/config/dto.php index 634b846..201d3eb 100644 --- a/src/config/dto.php +++ b/src/config/dto.php @@ -4,11 +4,8 @@ use Icefox\DTO\Factories\CollectionFactory; use Illuminate\Support\Collection; return [ + 'cast' => [], 'rules' => [ Collection::class => CollectionFactory::rules(...), ], - 'default' => [ - 'factories' => [ - ], - ], ]; diff --git a/tests/Classes/WithMapperObject.php b/tests/Classes/WithMapperObject.php index 31e0bd3..899b8fe 100644 --- a/tests/Classes/WithMapperObject.php +++ b/tests/Classes/WithMapperObject.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace Tests\Classes; use Carbon\CarbonPeriodImmutable; -use Icefox\DTO\Attributes\FromMapper; +use Icefox\DTO\Attributes\CastWith; use Icefox\DTO\DataObject; readonly class WithMapperObject @@ -13,7 +13,7 @@ readonly class WithMapperObject use DataObject; public function __construct( - #[FromMapper(CarbonPeriodMapper::class)] + #[CastWith(CarbonPeriodMapper::class)] public CarbonPeriodImmutable $period, ) {} }