This commit is contained in:
icefox 2026-02-18 20:18:39 -03:00
parent bef42b3352
commit afb47c1977
No known key found for this signature in database
7 changed files with 56 additions and 26 deletions

View file

@ -7,7 +7,7 @@ namespace Icefox\DTO\Attributes;
use Attribute; use Attribute;
#[Attribute(Attribute::TARGET_PARAMETER)] #[Attribute(Attribute::TARGET_PARAMETER)]
class FromMapper class CastWith
{ {
/** /**
* @param class-string $class * @param class-string $class

18
src/Config.php Normal file
View file

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace Icefox\DTO;
class Config
{
public static function getCaster(string $className): ?callable
{
return config('dto.cast.' . $className, null);
}
public static function getRules(string $className): ?callable
{
return config('dto.rules.' . $className, null);
}
}

View file

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace Icefox\DTO; namespace Icefox\DTO;
use Icefox\DTO\Attributes\FromInput; use Icefox\DTO\Attributes\FromInput;
use Icefox\DTO\Attributes\FromMapper; use Icefox\DTO\Attributes\CastWith;
use Icefox\DTO\Attributes\FromRouteParameter; use Icefox\DTO\Attributes\FromRouteParameter;
use Icefox\DTO\Support\RuleFactory; use Icefox\DTO\Support\RuleFactory;
use Icefox\DTO\Support\ValueFactory; use Icefox\DTO\Support\ValueFactory;
@ -78,7 +78,7 @@ trait DataObject
foreach ($parameters as $parameter) { foreach ($parameters as $parameter) {
$parameterName = $parameter->reflection->getName(); $parameterName = $parameter->reflection->getName();
if ($mapper = array_first($parameter->reflection->getAttributes(FromMapper::class))) { if ($mapper = array_first($parameter->reflection->getAttributes(CastWith::class))) {
$value = App::call( $value = App::call(
[App::make($mapper->newInstance()->class), 'map'], [App::make($mapper->newInstance()->class), 'map'],
['value' => $validator->getValue($parameterName)], ['value' => $validator->getValue($parameterName)],

View file

@ -4,6 +4,8 @@ declare(strict_types=1);
namespace Icefox\DTO\Support; namespace Icefox\DTO\Support;
use Icefox\DTO\Attributes\CastWith;
use Icefox\DTO\Config;
use Icefox\DTO\Attributes\FromMapper; use Icefox\DTO\Attributes\FromMapper;
use Icefox\DTO\ParameterMeta; use Icefox\DTO\ParameterMeta;
use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\App;
@ -139,7 +141,7 @@ class RuleFactory
} }
if ($type instanceof ReflectionNamedType && $name = $type->getName()) { 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) { foreach ($globalRules($parameter->reflection, $parameter->tag->getType()) as $scopedPrefix => $values) {
$realPrefix = $root . $scopedPrefix; $realPrefix = $root . $scopedPrefix;
$rules[$realPrefix] = array_values(array_unique(array_merge($rules[$realPrefix] ?? [], $values))); $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; $mapperClass = $attr->newInstance()->class;
if (method_exists($mapperClass, 'rules')) { if (method_exists($mapperClass, 'rules')) {
$subRules = App::call("$mapperClass@rules"); $subRules = App::call("$mapperClass@rules");

View file

@ -4,6 +4,8 @@ declare(strict_types=1);
namespace Icefox\DTO\Support; namespace Icefox\DTO\Support;
use Icefox\DTO\Attributes\CastWith;
use Icefox\DTO\Config;
use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\App;
use ReflectionNamedType; use ReflectionNamedType;
use ReflectionParameter; use ReflectionParameter;
@ -20,7 +22,7 @@ class ValueFactory
{ {
public static function constructObject(string $className, mixed $rawValue): object public static function constructObject(string $className, mixed $rawValue): object
{ {
if ($mapper = config('dto.mappers.' . $className, null)) { if ($mapper = Config::getCaster($className)) {
return $mapper($rawValue); return $mapper($rawValue);
} }
@ -83,21 +85,32 @@ class ValueFactory
return null; return null;
} }
if (is_null($type)) { $castWithAttrs = $reflection->getAttributes(CastWith::class);
$reflectedType = $reflection->getType(); if ($withCast = $reflection->getAttributes(CastWith::class)[0] ?? null) {
if ($reflectedType instanceof ReflectionNamedType && $name = $reflectedType->getName()) { $caster = $withCast->newInstance()->class;
return match ($name) { return App::call("$caster@cast", ['value' => $rawValue]);
'string' => $rawValue,
'bool' => boolval($rawValue),
'int' => intval($rawValue),
'float' => floatval($rawValue),
'array' => $rawValue,
default => self::constructObject($name, $rawValue),
};
}
return $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;
} }
} }

View file

@ -4,11 +4,8 @@ use Icefox\DTO\Factories\CollectionFactory;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
return [ return [
'cast' => [],
'rules' => [ 'rules' => [
Collection::class => CollectionFactory::rules(...), Collection::class => CollectionFactory::rules(...),
], ],
'default' => [
'factories' => [
],
],
]; ];

View file

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace Tests\Classes; namespace Tests\Classes;
use Carbon\CarbonPeriodImmutable; use Carbon\CarbonPeriodImmutable;
use Icefox\DTO\Attributes\FromMapper; use Icefox\DTO\Attributes\CastWith;
use Icefox\DTO\DataObject; use Icefox\DTO\DataObject;
readonly class WithMapperObject readonly class WithMapperObject
@ -13,7 +13,7 @@ readonly class WithMapperObject
use DataObject; use DataObject;
public function __construct( public function __construct(
#[FromMapper(CarbonPeriodMapper::class)] #[CastWith(CarbonPeriodMapper::class)]
public CarbonPeriodImmutable $period, public CarbonPeriodImmutable $period,
) {} ) {}
} }