Skip to content

Commit

Permalink
fix: inheritance support in property mapper (#76)
Browse files Browse the repository at this point in the history
* fix: inheritance support in property mapper

* the fix
  • Loading branch information
priyadi committed Jun 21, 2024
1 parent 25a52c0 commit 1c66ead
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 21 deletions.
31 changes: 10 additions & 21 deletions src/CustomMapper/Implementation/PropertyMapperResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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];
}
}
}

Expand Down
18 changes: 18 additions & 0 deletions tests/Fixtures/PropertyMapper/ChildOfSomeObject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

/*
* This file is part of rekalogika/mapper package.
*
* (c) Priyadi Iman Nurcahyo <https://rekalogika.dev>
*
* 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
{
}
18 changes: 18 additions & 0 deletions tests/Fixtures/PropertyMapper/ChildOfSomeObjectDto.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

/*
* This file is part of rekalogika/mapper package.
*
* (c) Priyadi Iman Nurcahyo <https://rekalogika.dev>
*
* 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
{
}
53 changes: 53 additions & 0 deletions tests/IntegrationTest/PropertyMappingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit 1c66ead

Please sign in to comment.