From a670d98ed4333d6e840639355c9918696036f0e1 Mon Sep 17 00:00:00 2001 From: Priyadi Iman Nurcahyo <1102197+priyadi@users.noreply.github.com> Date: Tue, 24 Sep 2024 09:54:44 +0700 Subject: [PATCH 1/2] chore: cleanup property path classes --- CHANGELOG.md | 1 + psalm.xml | 4 +- ...thAwarePropertyTypeExtractorException.php} | 5 +- .../ObjectToObjectMetadataFactory.php | 11 ++-- ...solver.php => PropertyMetadataFactory.php} | 22 +++---- ...ropertyPathAwarePropertyTypeExtractor.php} | 61 +++++++++++++------ tests/src/Fixtures/MapPropertyPath/Book.php | 6 +- .../src/Fixtures/MapPropertyPath/Library.php | 2 +- tests/src/Fixtures/MapPropertyPath/Shelf.php | 2 +- .../IntegrationTest/MapPropertyPathTest.php | 17 +++--- 10 files changed, 70 insertions(+), 61 deletions(-) rename src/Transformer/Exception/{PropertyPathResolverException.php => PropertyPathAwarePropertyTypeExtractorException.php} (82%) rename src/Transformer/ObjectToObjectMetadata/Implementation/Util/{PropertyMetadataResolver.php => PropertyMetadataFactory.php} (96%) rename src/Transformer/ObjectToObjectMetadata/Implementation/Util/{PropertyPathResolver.php => PropertyPathAwarePropertyTypeExtractor.php} (59%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79717e9..185beec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * feat: property path support in `Map` attributes * fix: writing using property path * fix: data collector output for property path +* chore: cleanup property path classes ## 1.8.0 diff --git a/psalm.xml b/psalm.xml index cbfa928..a09e026 100644 --- a/psalm.xml +++ b/psalm.xml @@ -72,14 +72,14 @@ - + - + diff --git a/src/Transformer/Exception/PropertyPathResolverException.php b/src/Transformer/Exception/PropertyPathAwarePropertyTypeExtractorException.php similarity index 82% rename from src/Transformer/Exception/PropertyPathResolverException.php rename to src/Transformer/Exception/PropertyPathAwarePropertyTypeExtractorException.php index 74098e2..e04a084 100644 --- a/src/Transformer/Exception/PropertyPathResolverException.php +++ b/src/Transformer/Exception/PropertyPathAwarePropertyTypeExtractorException.php @@ -18,11 +18,8 @@ /** * @internal */ -class PropertyPathResolverException extends \LogicException implements ExceptionInterface +class PropertyPathAwarePropertyTypeExtractorException extends \LogicException implements ExceptionInterface { - /** - * @param class-string $class - */ public function __construct(string $message, string $class, string $propertyPath) { $message = \sprintf('%s, root class: "%s", property path: "%s"', $message, $class, $propertyPath); diff --git a/src/Transformer/ObjectToObjectMetadata/Implementation/ObjectToObjectMetadataFactory.php b/src/Transformer/ObjectToObjectMetadata/Implementation/ObjectToObjectMetadataFactory.php index 93c9f92..dfd22b0 100644 --- a/src/Transformer/ObjectToObjectMetadata/Implementation/ObjectToObjectMetadataFactory.php +++ b/src/Transformer/ObjectToObjectMetadata/Implementation/ObjectToObjectMetadataFactory.php @@ -21,7 +21,7 @@ use Rekalogika\Mapper\Transformer\Exception\InternalClassUnsupportedException; use Rekalogika\Mapper\Transformer\Exception\SourceClassNotInInheritanceMapException; use Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\Implementation\Util\PropertyMappingResolver; -use Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\Implementation\Util\PropertyMetadataResolver; +use Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\Implementation\Util\PropertyMetadataFactory; use Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\ObjectToObjectMetadata; use Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\ObjectToObjectMetadataFactoryInterface; use Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\PropertyMapping; @@ -38,7 +38,8 @@ */ final readonly class ObjectToObjectMetadataFactory implements ObjectToObjectMetadataFactoryInterface { - private PropertyMetadataResolver $propertyMetadataResolver; + private PropertyMetadataFactory $propertyMetadataFactory; + private PropertyMappingResolver $propertyMappingResolver; public function __construct( @@ -51,7 +52,7 @@ public function __construct( private ProxyFactoryInterface $proxyFactory, TypeResolverInterface $typeResolver, ) { - $this->propertyMetadataResolver = new PropertyMetadataResolver( + $this->propertyMetadataFactory = new PropertyMetadataFactory( propertyReadInfoExtractor: $propertyReadInfoExtractor, propertyWriteInfoExtractor: $propertyWriteInfoExtractor, propertyTypeExtractor: $propertyTypeExtractor, @@ -192,14 +193,14 @@ public function createObjectToObjectMetadata( // generate source & target property metadata - $sourcePropertyMetadata = $this->propertyMetadataResolver + $sourcePropertyMetadata = $this->propertyMetadataFactory ->createSourcePropertyMetadata( class: $sourceClass, property: $sourceProperty, allowsDynamicProperties: $sourceAllowsDynamicProperties, ); - $targetPropertyMetadata = $this->propertyMetadataResolver + $targetPropertyMetadata = $this->propertyMetadataFactory ->createTargetPropertyMetadata( class: $targetClass, property: $targetProperty, diff --git a/src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyMetadataResolver.php b/src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyMetadataFactory.php similarity index 96% rename from src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyMetadataResolver.php rename to src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyMetadataFactory.php index 8e77479..03a6644 100644 --- a/src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyMetadataResolver.php +++ b/src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyMetadataFactory.php @@ -33,18 +33,18 @@ /** * @internal */ -final readonly class PropertyMetadataResolver +final readonly class PropertyMetadataFactory { - private PropertyPathResolver $propertyPathResolver; + private PropertyTypeExtractorInterface $propertyTypeExtractor; public function __construct( private PropertyReadInfoExtractorInterface $propertyReadInfoExtractor, private PropertyWriteInfoExtractorInterface $propertyWriteInfoExtractor, - private PropertyTypeExtractorInterface $propertyTypeExtractor, + PropertyTypeExtractorInterface $propertyTypeExtractor, private TypeResolverInterface $typeResolver, ) { - $this->propertyPathResolver = new PropertyPathResolver( - propertyTypeExtractor: $propertyTypeExtractor, + $this->propertyTypeExtractor = new PropertyPathAwarePropertyTypeExtractor( + decorated: $propertyTypeExtractor, ); } @@ -57,10 +57,7 @@ public function createSourcePropertyMetadata( bool $allowsDynamicProperties, ): SourcePropertyMetadata { if ($this->isPropertyPath($property)) { - $types = $this->propertyPathResolver->resolvePropertyPath( - class: $class, - propertyPath: $property, - ); + $types = array_values($this->propertyTypeExtractor->getTypes($class, $property) ?? []); return new SourcePropertyMetadata( readMode: ReadMode::PropertyPath, @@ -106,10 +103,7 @@ public function createTargetPropertyMetadata( bool $allowsDynamicProperties, ): TargetPropertyMetadata { if ($this->isPropertyPath($property)) { - $types = $this->propertyPathResolver->resolvePropertyPath( - class: $class, - propertyPath: $property, - ); + $types = array_values($this->propertyTypeExtractor->getTypes($class, $property) ?? []); return new TargetPropertyMetadata( readMode: ReadMode::PropertyPath, @@ -318,7 +312,6 @@ private function processPropertyWriteInfo( /** * @param class-string $class - * @return boolean */ private function sourceAllowsTargetDelete( string $class, @@ -347,7 +340,6 @@ class: $class, /** * @param class-string $class - * @return boolean */ private function targetAllowsDelete( string $class, diff --git a/src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyPathResolver.php b/src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyPathAwarePropertyTypeExtractor.php similarity index 59% rename from src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyPathResolver.php rename to src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyPathAwarePropertyTypeExtractor.php index ffe5b76..67c5cdb 100644 --- a/src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyPathResolver.php +++ b/src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyPathAwarePropertyTypeExtractor.php @@ -13,30 +13,48 @@ namespace Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\Implementation\Util; -use Rekalogika\Mapper\Transformer\Exception\PropertyPathResolverException; +use Rekalogika\Mapper\Transformer\Exception\PropertyPathAwarePropertyTypeExtractorException; use Symfony\Component\PropertyAccess\PropertyPath; use Symfony\Component\PropertyAccess\PropertyPathIteratorInterface; use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; use Symfony\Component\PropertyInfo\Type; +use Symfony\Component\TypeInfo\Type as TypeInfoType; /** * @internal */ -final readonly class PropertyPathResolver +final readonly class PropertyPathAwarePropertyTypeExtractor implements PropertyTypeExtractorInterface { public function __construct( - private PropertyTypeExtractorInterface $propertyTypeExtractor, + private PropertyTypeExtractorInterface $decorated, ) {} /** - * @param class-string $class - * @return list + * @param array $context */ - public function resolvePropertyPath( + public function getType( string $class, - string $propertyPath, - ): array { - $propertyPathObject = new PropertyPath($propertyPath); + string $property, + array $context = [], + ): TypeInfoType { + throw new \BadMethodCallException('Not implemented'); + } + + /** + * @param array $context + * @return null|array + */ + #[\Override] + public function getTypes( + string $class, + string $property, + array $context = [], + ): ?array { + if (!$this->isPropertyPath($property)) { + return $this->decorated->getTypes($class, $property, $context); + } + + $propertyPathObject = new PropertyPath($property); /** @var \Iterator&PropertyPathIteratorInterface */ $iterator = $propertyPathObject->getIterator(); @@ -55,10 +73,10 @@ public function resolvePropertyPath( if ($types !== null) { if (\count($types) > 1) { - throw new PropertyPathResolverException( + throw new PropertyPathAwarePropertyTypeExtractorException( message: \sprintf('Cannot proceed because property "%s" has multiple types in class "%s"', $propertyPathPart, $currentClass ?? 'unknown'), class: $class, - propertyPath: $propertyPath, + propertyPath: $property, ); } @@ -71,33 +89,38 @@ class: $class, $types = $currentType?->getCollectionValueTypes(); } else { if ($currentClass === null) { - throw new PropertyPathResolverException( + throw new PropertyPathAwarePropertyTypeExtractorException( message: \sprintf('Trying to resolve path "%s", but the current node is not an object', $propertyPathPart), class: $class, - propertyPath: $propertyPath, + propertyPath: $property, ); } $currentPath .= '.' . $propertyPathPart; - $types = $this->propertyTypeExtractor - ->getTypes($currentClass, $propertyPathPart); + $types = $this->decorated + ->getTypes($currentClass, $propertyPathPart, $context); } if ($types === null) { - throw new PropertyPathResolverException( + throw new PropertyPathAwarePropertyTypeExtractorException( message: \sprintf('Property "%s" not found in class "%s"', $propertyPathPart, $currentClass ?? 'unknown'), class: $class, - propertyPath: $propertyPath, + propertyPath: $property, ); } elseif (\count($types) === 0) { - throw new PropertyPathResolverException( + throw new PropertyPathAwarePropertyTypeExtractorException( message: \sprintf('Cannot determine the type of property "%s" in class "%s"', $propertyPathPart, $currentClass ?? 'unknown'), class: $class, - propertyPath: $propertyPath, + propertyPath: $property, ); } } return array_values($types ?? []); } + + private function isPropertyPath(string $property): bool + { + return str_contains($property, '.') || str_contains($property, '['); + } } diff --git a/tests/src/Fixtures/MapPropertyPath/Book.php b/tests/src/Fixtures/MapPropertyPath/Book.php index bc46585..e1f6653 100644 --- a/tests/src/Fixtures/MapPropertyPath/Book.php +++ b/tests/src/Fixtures/MapPropertyPath/Book.php @@ -23,7 +23,7 @@ final class Book /** * @var Collection */ - private Collection $chapters; + private readonly Collection $chapters; public function __construct() { @@ -70,8 +70,4 @@ public function getParts(): Collection { return new ArrayCollection(); } - - public function addPart(Chapter|Section $chapter): void {} - - public function removePart(Chapter|Section $chapter): void {} } diff --git a/tests/src/Fixtures/MapPropertyPath/Library.php b/tests/src/Fixtures/MapPropertyPath/Library.php index 9ec9194..fbf94a9 100644 --- a/tests/src/Fixtures/MapPropertyPath/Library.php +++ b/tests/src/Fixtures/MapPropertyPath/Library.php @@ -21,7 +21,7 @@ final class Library /** * @var Collection */ - private Collection $shelves; + private readonly Collection $shelves; private ?string $name = null; diff --git a/tests/src/Fixtures/MapPropertyPath/Shelf.php b/tests/src/Fixtures/MapPropertyPath/Shelf.php index e0ce466..87cf79f 100644 --- a/tests/src/Fixtures/MapPropertyPath/Shelf.php +++ b/tests/src/Fixtures/MapPropertyPath/Shelf.php @@ -23,7 +23,7 @@ final class Shelf /** * @var Collection */ - private Collection $books; + private readonly Collection $books; private ?int $number = null; diff --git a/tests/src/IntegrationTest/MapPropertyPathTest.php b/tests/src/IntegrationTest/MapPropertyPathTest.php index 269a118..d6af7f4 100644 --- a/tests/src/IntegrationTest/MapPropertyPathTest.php +++ b/tests/src/IntegrationTest/MapPropertyPathTest.php @@ -23,8 +23,8 @@ use Rekalogika\Mapper\Tests\Fixtures\MapPropertyPath\Shelf; use Rekalogika\Mapper\Tests\Fixtures\MapPropertyPathDto\BookDto; use Rekalogika\Mapper\Tests\Fixtures\MapPropertyPathDto\ChapterDto; -use Rekalogika\Mapper\Transformer\Exception\PropertyPathResolverException; -use Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\Implementation\Util\PropertyPathResolver; +use Rekalogika\Mapper\Transformer\Exception\PropertyPathAwarePropertyTypeExtractorException; +use Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\Implementation\Util\PropertyPathAwarePropertyTypeExtractor; use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; use Symfony\Component\PropertyInfo\Type; @@ -33,9 +33,9 @@ class MapPropertyPathTest extends FrameworkTestCase /** * @param class-string $class * @param list|class-string $expected - * @dataProvider propertyPathResolverDataProvider + * @dataProvider propertyPathAwarePropertyTypeExtractorDataProvider */ - public function testPropertyPathResolver( + public function testPropertyPathAwarePropertyTypeExtractor( string $class, string $path, array|string $expected, @@ -44,8 +44,7 @@ public function testPropertyPathResolver( $this->expectException($expected); } - $propertyTypeExtractor = $this->get(PropertyTypeExtractorInterface::class); - $propertyPathResolver = new PropertyPathResolver($propertyTypeExtractor); + $propertyTypeExtractor = new PropertyPathAwarePropertyTypeExtractor($this->get(PropertyTypeExtractorInterface::class)); $chapter = new Chapter(); @@ -58,7 +57,7 @@ public function testPropertyPathResolver( $library = new Library(); $library->addShelf($shelf); - $type = $propertyPathResolver->resolvePropertyPath($class, $path); + $type = $propertyTypeExtractor->getTypes($class, $path); $this->assertEquals($expected, $type); } @@ -66,7 +65,7 @@ public function testPropertyPathResolver( /** * @return iterable|class-string}> */ - public static function propertyPathResolverDataProvider(): iterable + public static function propertyPathAwarePropertyTypeExtractorDataProvider(): iterable { yield [ Book::class, @@ -141,7 +140,7 @@ class: Chapter::class, yield [ Chapter::class, 'book.shelf.library.foo', - PropertyPathResolverException::class, + PropertyPathAwarePropertyTypeExtractorException::class, ]; yield [ From fb9f088df46d0d2ae1945b92a9c2b1b05edf0aa6 Mon Sep 17 00:00:00 2001 From: Priyadi Iman Nurcahyo <1102197+priyadi@users.noreply.github.com> Date: Tue, 24 Sep 2024 09:59:11 +0700 Subject: [PATCH 2/2] 6.4 compat --- .../Util/PropertyPathAwarePropertyTypeExtractor.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyPathAwarePropertyTypeExtractor.php b/src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyPathAwarePropertyTypeExtractor.php index 67c5cdb..004f752 100644 --- a/src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyPathAwarePropertyTypeExtractor.php +++ b/src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyPathAwarePropertyTypeExtractor.php @@ -31,12 +31,13 @@ public function __construct( /** * @param array $context + * @return TypeInfoType */ public function getType( string $class, string $property, array $context = [], - ): TypeInfoType { + ) { throw new \BadMethodCallException('Not implemented'); }