feat: DTO에 DefaultValue, DefaultValueGenerator 추가

This commit is contained in:
2022-05-28 15:53:06 +09:00
parent 08eae297fd
commit cb3982e57c
5 changed files with 150 additions and 13 deletions
+16
View File
@@ -0,0 +1,16 @@
<?php
namespace sammo\DTO\Attr;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class DefaultValue
{
public function __construct(public readonly null|bool|int|float|string|array $defaultValue)
{
}
public function getDefaultValue(): mixed
{
return $this->defaultValue;
}
}
@@ -0,0 +1,20 @@
<?php
namespace sammo\DTO\Attr;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class DefaultValueGenerator
{
public function __construct(public readonly string $generator)
{
if (!is_callable($generator)) {
throw new \Exception("$generator is not a callable");
}
}
public function getDefaultValue(): mixed
{
$generator = $this->generator;
return $generator();
}
}
+41 -4
View File
@@ -11,7 +11,6 @@ abstract class DTO
public static function fromArray(\ArrayAccess|array $array): static public static function fromArray(\ArrayAccess|array $array): static
{ {
$reflection = new \ReflectionClass(static::class); $reflection = new \ReflectionClass(static::class);
$args = [];
if ($array instanceof \ArrayAccess) { if ($array instanceof \ArrayAccess) {
$keyExists = fn (string|int $key) => $array->offsetExists($key); $keyExists = fn (string|int $key) => $array->offsetExists($key);
@@ -19,6 +18,11 @@ abstract class DTO
$keyExists = fn (string|int $key) => array_key_exists($key, $array); $keyExists = fn (string|int $key) => array_key_exists($key, $array);
} }
$params = Util\Util::getConstructorParams($reflection);
$args = [];
$lazyMap = [];
foreach ($reflection->getProperties( foreach ($reflection->getProperties(
\ReflectionProperty::IS_PUBLIC \ReflectionProperty::IS_PUBLIC
) as $property) { ) as $property) {
@@ -26,6 +30,19 @@ abstract class DTO
$name = $property->getName(); $name = $property->getName();
$rawName = $name; $rawName = $name;
$param = $params[$name] ?? null;
if (key_exists(Attr\DefaultValueGenerator::class, $attrs)){
/** @var Attr\DefaultValueGenerator */
$defaultValueSetter = $attrs[Attr\DefaultValueGenerator::class]->newInstance();
} else if (key_exists(Attr\DefaultValue::class, $attrs)) {
/** @var Attr\DefaultValue */
$defaultValueSetter = $attrs[Attr\DefaultValue::class]->newInstance();
}
else{
$defaultValueSetter = null;
}
if (key_exists(Attr\RawName::class, $attrs)) { if (key_exists(Attr\RawName::class, $attrs)) {
$rawAttr = $attrs[Attr\RawName::class]; $rawAttr = $attrs[Attr\RawName::class];
$attr = new Attr\RawName(...$rawAttr->getArguments()); $attr = new Attr\RawName(...$rawAttr->getArguments());
@@ -33,13 +50,25 @@ abstract class DTO
} }
if (!$keyExists($rawName)) { if (!$keyExists($rawName)) {
if ($property->hasDefaultValue()) { if ($param !== null && $param->isOptional()){
$args[$name] = $property->getDefaultValue(); $defaultValue = $param->getDefaultValue();
}
else if ($property->hasDefaultValue()) {
$defaultValue = $property->getDefaultValue();
} else if($defaultValueSetter !== null){
$defaultValue = $defaultValueSetter->getDefaultValue();
} else if ($property->getType()->allowsNull()) { } else if ($property->getType()->allowsNull()) {
$args[$name] = null; $defaultValue = null;
} else { } else {
throw new \Exception("Missing property: {$name}"); throw new \Exception("Missing property: {$name}");
} }
if($param !== null){
$args[$name] = $defaultValue;
}
else{
$lazyMap[$name] = $defaultValue;
}
continue; continue;
} }
@@ -60,10 +89,18 @@ abstract class DTO
} }
$value = $converter->convertFrom($value); $value = $converter->convertFrom($value);
if($param !== null){
$args[$name] = $value; $args[$name] = $value;
} }
else{
$lazyMap[$name] = $value;
}
}
$object = $reflection->newInstanceArgs($args); $object = $reflection->newInstanceArgs($args);
foreach($lazyMap as $name => $value){
$object->{$name} = $value;
}
return $object; return $object;
} }
+15
View File
@@ -20,6 +20,21 @@ class Util{
return $result; return $result;
} }
/**
* @return array<string,\ReflectionParameter>
*/
public static function getConstructorParams(\ReflectionClass $class): array{
$constructor = $class->getConstructor();
if($constructor === null){
return [];
}
$result = [];
foreach($constructor->getParameters() as $param){
$result[$param->getName()] = $param;
}
return $result;
}
/** /**
* @return array<string> * @return array<string>
*/ */
+57 -8
View File
@@ -1,6 +1,8 @@
<?php <?php
use sammo\DTO\Attr\Convert; use sammo\DTO\Attr\Convert;
use sammo\DTO\Attr\DefaultValue;
use sammo\DTO\Attr\DefaultValueGenerator;
use sammo\DTO\DTO; use sammo\DTO\DTO;
use sammo\DTO\Attr\JsonString; use sammo\DTO\Attr\JsonString;
use sammo\DTO\Attr\NullIsUndefined; use sammo\DTO\Attr\NullIsUndefined;
@@ -150,14 +152,13 @@ class TypeRawName extends DTO
public int $argName, public int $argName,
#[RawName('vID')] #[RawName('vID')]
public int $vID, public int $vID,
) ) {
{
} }
} }
class TypeDateTime extends DTO{ class TypeDateTime extends DTO
{
public function __construct( public function __construct(
#[Convert(DateTimeConverter::class)] #[Convert(DateTimeConverter::class)]
public \DateTimeImmutable $a, public \DateTimeImmutable $a,
@@ -171,6 +172,30 @@ class TypeDateTime extends DTO{
} }
} }
function returnDateTime()
{
return new \DateTimeImmutable('2022-04-01 00:00:00');
}
class TypeDefaultValue extends DTO
{
public bool $g = true;
public function __construct(
public ?string $a,
#[DefaultValue(false)]
public bool $b,
public int $c,
#[DefaultValue([1,2,3])]
public array $e,
#[DefaultValueGenerator('returnDateTime')]
#[Convert(DateTimeConverter::class)]
public \DateTimeInterface $d,
public int $f = 111, //contstruct의 뒤에서 default 값이 설정될 경우에만
) {
}
}
class DTOTest extends PHPUnit\Framework\TestCase class DTOTest extends PHPUnit\Framework\TestCase
{ {
public function testBasic() public function testBasic()
@@ -321,7 +346,8 @@ class DTOTest extends PHPUnit\Framework\TestCase
$this->assertEquals($rawType, $testType); $this->assertEquals($rawType, $testType);
} }
public function testMap(){ public function testMap()
{
$rawType = [ $rawType = [
'a' => 1, 'a' => 1,
'b' => [ 'b' => [
@@ -336,7 +362,8 @@ class DTOTest extends PHPUnit\Framework\TestCase
$this->assertEquals($rawType, $testType); $this->assertEquals($rawType, $testType);
} }
public function testNestedMap(){ public function testNestedMap()
{
$rawType = [ $rawType = [
'a' => 1, 'a' => 1,
'b' => [ 'b' => [
@@ -357,7 +384,8 @@ class DTOTest extends PHPUnit\Framework\TestCase
$this->assertEquals($rawType, $testType); $this->assertEquals($rawType, $testType);
} }
public function testRawName(){ public function testRawName()
{
$rawType = [ $rawType = [
'arg_name' => 1, 'arg_name' => 1,
'vID' => 2, 'vID' => 2,
@@ -368,7 +396,8 @@ class DTOTest extends PHPUnit\Framework\TestCase
$this->assertEquals($rawType, $testType); $this->assertEquals($rawType, $testType);
} }
public function testDateTime(){ public function testDateTime()
{
$rawType = [ $rawType = [
'a' => '2022-01-01 10:11:22', 'a' => '2022-01-01 10:11:22',
'b' => '2022-02-01T12:34:56.1234+09:00', 'b' => '2022-02-01T12:34:56.1234+09:00',
@@ -387,4 +416,24 @@ class DTOTest extends PHPUnit\Framework\TestCase
$this->assertEquals($testValue, $testType); $this->assertEquals($testValue, $testType);
} }
public function testDefaultValue()
{
$rawType = [
'c' => 3,
];
$testValue = [
'g' => true,
'a' => null,
'b' => false,
'c' => 3,
'e' => [1, 2, 3],
'd' => '2022-04-01 00:00:00',
'f' => 111,
];
$obj = TypeDefaultValue::fromArray($rawType);
$testType = $obj->toArray();
$this->assertEquals($testValue, $testType);
}
} }