diff --git a/src/CustomMapper/Implementation/PropertyMapperResolver.php b/src/CustomMapper/Implementation/PropertyMapperResolver.php index 3fb70a5..f10bebd 100644 --- a/src/CustomMapper/Implementation/PropertyMapperResolver.php +++ b/src/CustomMapper/Implementation/PropertyMapperResolver.php @@ -15,9 +15,12 @@ use Rekalogika\Mapper\CustomMapper\PropertyMapperResolverInterface; use Rekalogika\Mapper\ServiceMethod\ServiceMethodSpecification; +use Rekalogika\Mapper\Util\ClassUtil; /** * @internal + * + * Not in hot path, no caching needed. */ final class PropertyMapperResolver implements PropertyMapperResolverInterface { @@ -52,28 +55,14 @@ public function getPropertyMapper( string $targetClass, string $property ): ?ServiceMethodSpecification { - if (!isset($this->propertyMappers[$targetClass][$property])) { - return null; - } - - $propertyMappers = $this->propertyMappers[$targetClass][$property]; - - $sourceClassReflection = new \ReflectionClass($sourceClass); - - do { - if (isset($propertyMappers[$sourceClassReflection->getName()])) { - return $propertyMappers[$sourceClassReflection->getName()]; - } - } while ($sourceClassReflection = $sourceClassReflection->getParentClass()); - - $interfaces = class_implements($sourceClass); - if ($interfaces === false) { - return null; - } + $sourceClasses = ClassUtil::getAllClassesFromObject($sourceClass); + $targetClasses = ClassUtil::getAllClassesFromObject($targetClass); - foreach ($interfaces as $interface) { - if (isset($propertyMappers[$interface])) { - return $propertyMappers[$interface]; + foreach ($sourceClasses as $sourceClass) { + foreach ($targetClasses as $targetClass) { + if (isset($this->propertyMappers[$targetClass][$property][$sourceClass])) { + return $this->propertyMappers[$targetClass][$property][$sourceClass]; + } } } diff --git a/tests/Fixtures/PropertyMapper/ChildOfSomeObject.php b/tests/Fixtures/PropertyMapper/ChildOfSomeObject.php new file mode 100644 index 0000000..f69bd71 --- /dev/null +++ b/tests/Fixtures/PropertyMapper/ChildOfSomeObject.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +namespace Rekalogika\Mapper\Tests\Fixtures\PropertyMapper; + +class ChildOfSomeObject extends SomeObject +{ +} diff --git a/tests/Fixtures/PropertyMapper/ChildOfSomeObjectDto.php b/tests/Fixtures/PropertyMapper/ChildOfSomeObjectDto.php new file mode 100644 index 0000000..1014c65 --- /dev/null +++ b/tests/Fixtures/PropertyMapper/ChildOfSomeObjectDto.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +namespace Rekalogika\Mapper\Tests\Fixtures\PropertyMapper; + +class ChildOfSomeObjectDto extends SomeObjectDto +{ +} diff --git a/tests/IntegrationTest/PropertyMappingTest.php b/tests/IntegrationTest/PropertyMappingTest.php index a0edea8..fca89b1 100644 --- a/tests/IntegrationTest/PropertyMappingTest.php +++ b/tests/IntegrationTest/PropertyMappingTest.php @@ -18,6 +18,8 @@ use Rekalogika\Mapper\MainTransformer\Implementation\MainTransformer; use Rekalogika\Mapper\ServiceMethod\ServiceMethodSpecification; use Rekalogika\Mapper\Tests\Common\FrameworkTestCase; +use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\ChildOfSomeObject; +use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\ChildOfSomeObjectDto; use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\PropertyMapperWithClassAttribute; use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\PropertyMapperWithClassAttributeWithoutExplicitProperty; use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\PropertyMapperWithConstructorWithClassAttribute; @@ -162,6 +164,57 @@ public function testPropertyMapping(): void ), $dto->propertyE); } + public function testPropertyMappingChildToParent(): void + { + $object = new ChildOfSomeObject(); + $dto = $this->mapper->map($object, SomeObjectDto::class); + + $this->assertEquals(ChildOfSomeObject::class . '::propertyA', $dto->propertyA); + $this->assertEquals(ChildOfSomeObject::class . '::propertyB', $dto->propertyB); + $this->assertNull($dto->propertyC); + $this->assertEquals(ChildOfSomeObject::class . '::propertyD', $dto->propertyD); + $this->assertEquals(sprintf( + 'I have "%s" and "%s" that I can use to transform source property "%s"', + Context::class, + MainTransformer::class, + ChildOfSomeObject::class, + ), $dto->propertyE); + } + + public function testPropertyMappingChildToChild(): void + { + $object = new ChildOfSomeObject(); + $dto = $this->mapper->map($object, ChildOfSomeObjectDto::class); + + $this->assertEquals(ChildOfSomeObject::class . '::propertyA', $dto->propertyA); + $this->assertEquals(ChildOfSomeObject::class . '::propertyB', $dto->propertyB); + $this->assertNull($dto->propertyC); + $this->assertEquals(ChildOfSomeObject::class . '::propertyD', $dto->propertyD); + $this->assertEquals(sprintf( + 'I have "%s" and "%s" that I can use to transform source property "%s"', + Context::class, + MainTransformer::class, + ChildOfSomeObject::class, + ), $dto->propertyE); + } + + public function testPropertyMappingParentToChild(): void + { + $object = new SomeObject(); + $dto = $this->mapper->map($object, ChildOfSomeObjectDto::class); + + $this->assertEquals(SomeObject::class . '::propertyA', $dto->propertyA); + $this->assertEquals(SomeObject::class . '::propertyB', $dto->propertyB); + $this->assertNull($dto->propertyC); + $this->assertEquals(SomeObject::class . '::propertyD', $dto->propertyD); + $this->assertEquals(sprintf( + 'I have "%s" and "%s" that I can use to transform source property "%s"', + Context::class, + MainTransformer::class, + SomeObject::class, + ), $dto->propertyE); + } + public function testPropertyMappingWithConstructor(): void { $object = new SomeObject();