From 2a12c59326ee40b94383e2c67124e5e122db3607 Mon Sep 17 00:00:00 2001 From: jerodev Date: Wed, 10 May 2023 16:16:12 +0200 Subject: [PATCH 1/5] Implement `PostMapping` --- src/Attributes/PostMapping.php | 20 ++++++++++++++++++++ src/Objects/ClassBluePrint.php | 4 ++++ src/Objects/ClassBluePrinter.php | 6 ++++++ src/Objects/ObjectMapper.php | 12 ++++++++++++ 4 files changed, 42 insertions(+) create mode 100644 src/Attributes/PostMapping.php diff --git a/src/Attributes/PostMapping.php b/src/Attributes/PostMapping.php new file mode 100644 index 0000000..5b71298 --- /dev/null +++ b/src/Attributes/PostMapping.php @@ -0,0 +1,20 @@ + */ public array $properties = []; + + /** @var array */ + public array $classAttributes = []; } diff --git a/src/Objects/ClassBluePrinter.php b/src/Objects/ClassBluePrinter.php index b92ddf8..32873ac 100644 --- a/src/Objects/ClassBluePrinter.php +++ b/src/Objects/ClassBluePrinter.php @@ -27,6 +27,7 @@ public function print(string $class): ClassBluePrint $blueprint = new ClassBluePrint(); $this->printConstructor($reflection, $blueprint); $this->printProperties($reflection, $blueprint); + $this->printAttributes($reflection, $blueprint); return $blueprint; } @@ -93,6 +94,11 @@ private function printProperties(ReflectionClass $reflection, ClassBluePrint $bl } } + private function printAttributes(ReflectionClass $reflection, ClassBluePrint $blueprint): void + { + $blueprint->classAttributes = $reflection->getAttributes(); + } + private function resolveType(DataTypeCollection $type, string $className): DataTypeCollection { $baseClassName = \explode('\\', $className); diff --git a/src/Objects/ObjectMapper.php b/src/Objects/ObjectMapper.php index d9faed1..5d44d39 100644 --- a/src/Objects/ObjectMapper.php +++ b/src/Objects/ObjectMapper.php @@ -2,6 +2,7 @@ namespace Jerodev\DataMapper\Objects; +use Jerodev\DataMapper\Attributes\PostMapping; use Jerodev\DataMapper\Exceptions\CouldNotResolveClassException; use Jerodev\DataMapper\Mapper; use Jerodev\DataMapper\Types\DataType; @@ -74,6 +75,17 @@ private function createObjectMappingFunction(string $class, string $mapFunctionN $content.= \PHP_EOL . ' $x->' . $name . ' = ' . $propertyMap . ';'; } + // Post mapping functions? + foreach ($blueprint->classAttributes as $attribute) { + if ($attribute instanceof PostMapping) { + if (\is_string($attribute->postMappingCallback)) { + $content.= \PHP_EOL . \PHP_EOL . " \$x->{$attribute->postMappingCallback}(\$data, \$x);"; + } else { + $content.= \PHP_EOL . \PHP_EOL . " \call_user_func({$attribute->postMappingCallback}, \$data, \$x);"; + } + } + } + // Render the function $mapperClass = Mapper::class; return << Date: Wed, 10 May 2023 16:24:57 +0200 Subject: [PATCH 2/5] Test `PostMapping` attribute --- src/Objects/ClassBluePrinter.php | 7 ++++--- tests/MapperTest.php | 16 ++++++++++++++-- tests/_Mocks/SuperUserDto.php | 8 ++++++++ tests/_Mocks/UserDto.php | 8 ++++++++ 4 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 tests/_Mocks/SuperUserDto.php diff --git a/src/Objects/ClassBluePrinter.php b/src/Objects/ClassBluePrinter.php index 32873ac..2ac35a0 100644 --- a/src/Objects/ClassBluePrinter.php +++ b/src/Objects/ClassBluePrinter.php @@ -9,13 +9,11 @@ class ClassBluePrinter { - private readonly ClassResolver $classResolver; private readonly DocBlockParser $docBlockParser; private readonly DataTypeFactory $dataTypeFactory; public function __construct() { - $this->classResolver = new ClassResolver(); $this->dataTypeFactory = new DataTypeFactory(); $this->docBlockParser = new DocBlockParser(); } @@ -96,7 +94,10 @@ private function printProperties(ReflectionClass $reflection, ClassBluePrint $bl private function printAttributes(ReflectionClass $reflection, ClassBluePrint $blueprint): void { - $blueprint->classAttributes = $reflection->getAttributes(); + $blueprint->classAttributes = \array_map( + static fn (\ReflectionAttribute $ra) => $ra->newInstance(), + $reflection->getAttributes(), + ); } private function resolveType(DataTypeCollection $type, string $className): DataTypeCollection diff --git a/tests/MapperTest.php b/tests/MapperTest.php index 019b8b4..d500ce4 100644 --- a/tests/MapperTest.php +++ b/tests/MapperTest.php @@ -6,6 +6,7 @@ use Generator; use Jerodev\DataMapper\Mapper; use Jerodev\DataMapper\Tests\_Mocks\SuitEnum; +use Jerodev\DataMapper\Tests\_Mocks\SuperUserDto; use Jerodev\DataMapper\Tests\_Mocks\UserDto; use PHPUnit\Framework\TestCase; @@ -70,11 +71,22 @@ public static function objectValuesDataProvider(): Generator 'name' => 'Jeroen', 'favoriteSuit' => 'D', 'friends' => [ - ['name' => 'John'], - ['name' => 'Jane'], + ['name' => 'john'], + ['name' => 'jane'], ], ], $dto ]; + + $dto = new SuperUserDto('SuperMan'); + $dto->canFly = true; + yield [ + SuperUserDto::class, + [ + 'name' => 'SuperMan', + 'canFly' => true, + ], + $dto, + ]; } } diff --git a/tests/_Mocks/SuperUserDto.php b/tests/_Mocks/SuperUserDto.php new file mode 100644 index 0000000..58b1103 --- /dev/null +++ b/tests/_Mocks/SuperUserDto.php @@ -0,0 +1,8 @@ +name = $name; } + + public function post(): void + { + $this->name = \ucfirst($this->name); + } } From 10b6e11b584c90a2cc713e74ec297e69306a0d53 Mon Sep 17 00:00:00 2001 From: jerodev Date: Wed, 10 May 2023 16:28:27 +0200 Subject: [PATCH 3/5] Also check parent classes for relevant attributes --- src/Objects/ClassBluePrinter.php | 13 +++++++++---- tests/MapperTest.php | 4 ++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/Objects/ClassBluePrinter.php b/src/Objects/ClassBluePrinter.php index 2ac35a0..c0e97e6 100644 --- a/src/Objects/ClassBluePrinter.php +++ b/src/Objects/ClassBluePrinter.php @@ -2,6 +2,7 @@ namespace Jerodev\DataMapper\Objects; +use Jerodev\DataMapper\Attributes\PostMapping; use Jerodev\DataMapper\Types\DataType; use Jerodev\DataMapper\Types\DataTypeCollection; use Jerodev\DataMapper\Types\DataTypeFactory; @@ -94,10 +95,14 @@ private function printProperties(ReflectionClass $reflection, ClassBluePrint $bl private function printAttributes(ReflectionClass $reflection, ClassBluePrint $blueprint): void { - $blueprint->classAttributes = \array_map( - static fn (\ReflectionAttribute $ra) => $ra->newInstance(), - $reflection->getAttributes(), - ); + foreach ($reflection->getAttributes(PostMapping::class) as $attribute) { + $blueprint->classAttributes[] = $attribute->newInstance(); + } + + // Also check parent for relevant attributes + if ($reflection->getParentClass()) { + $this->printAttributes($reflection->getParentClass(), $blueprint); + } } private function resolveType(DataTypeCollection $type, string $className): DataTypeCollection diff --git a/tests/MapperTest.php b/tests/MapperTest.php index d500ce4..e5795ab 100644 --- a/tests/MapperTest.php +++ b/tests/MapperTest.php @@ -78,12 +78,12 @@ public static function objectValuesDataProvider(): Generator $dto ]; - $dto = new SuperUserDto('SuperMan'); + $dto = new SuperUserDto('Superman'); $dto->canFly = true; yield [ SuperUserDto::class, [ - 'name' => 'SuperMan', + 'name' => 'superman', 'canFly' => true, ], $dto, From 38b58520d556ccb82923b2c51dc7dec4daef9a89 Mon Sep 17 00:00:00 2001 From: jerodev Date: Wed, 10 May 2023 16:36:42 +0200 Subject: [PATCH 4/5] Allow multiple `PostMapping` attributes --- src/Attributes/PostMapping.php | 2 +- tests/MapperTest.php | 3 ++- tests/_Mocks/SuperUserDto.php | 11 +++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Attributes/PostMapping.php b/src/Attributes/PostMapping.php index 5b71298..75d1e54 100644 --- a/src/Attributes/PostMapping.php +++ b/src/Attributes/PostMapping.php @@ -10,7 +10,7 @@ * Can be either the name of a public function on the class, or a closure accepting the new object. * The function is gets the data array as a first parameter and the new object as the second. */ -#[Attribute(Attribute::TARGET_CLASS)] +#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] class PostMapping { public function __construct( diff --git a/tests/MapperTest.php b/tests/MapperTest.php index e5795ab..92b2e80 100644 --- a/tests/MapperTest.php +++ b/tests/MapperTest.php @@ -78,8 +78,9 @@ public static function objectValuesDataProvider(): Generator $dto ]; - $dto = new SuperUserDto('Superman'); + $dto = new SuperUserDto('Superman'); // Uppercase because UserDto post mapping $dto->canFly = true; + $dto->stars = 3; // Increased 3 times by post mapping function yield [ SuperUserDto::class, [ diff --git a/tests/_Mocks/SuperUserDto.php b/tests/_Mocks/SuperUserDto.php index 58b1103..c55fe8a 100644 --- a/tests/_Mocks/SuperUserDto.php +++ b/tests/_Mocks/SuperUserDto.php @@ -2,7 +2,18 @@ namespace Jerodev\DataMapper\Tests\_Mocks; +use Jerodev\DataMapper\Attributes\PostMapping; + +#[PostMapping('increaseStars')] +#[PostMapping('increaseStars')] +#[PostMapping('increaseStars')] class SuperUserDto extends UserDto { public bool $canFly = false; + public int $stars = 0; + + public function increaseStars(): void + { + $this->stars++; + } } From 6be0b4d066549895618bb4cb02c0c2c3cc239f3c Mon Sep 17 00:00:00 2001 From: jerodev Date: Wed, 10 May 2023 16:54:42 +0200 Subject: [PATCH 5/5] =?UTF-8?q?=E2=9D=A4=20trailing=20commas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpcs.xml | 8 ++++---- src/Attributes/PostMapping.php | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/phpcs.xml b/phpcs.xml index 9f5c9f1..e1d7670 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -1,8 +1,8 @@ - - - + + + src - \ No newline at end of file + diff --git a/src/Attributes/PostMapping.php b/src/Attributes/PostMapping.php index 75d1e54..299894b 100644 --- a/src/Attributes/PostMapping.php +++ b/src/Attributes/PostMapping.php @@ -14,7 +14,7 @@ class PostMapping { public function __construct( - public string|Closure $postMappingCallback + public string|Closure $postMappingCallback, ) { } }