Skip to content

Commit 30a6720

Browse files
committed
fix(constructor): fixed circular references with promoted properties
Circular references aren't handled with promoted properties Altered the statement to properly handle this case. Solves #271
1 parent 321cc75 commit 30a6720

File tree

6 files changed

+102
-0
lines changed

6 files changed

+102
-0
lines changed

src/Generator/MapMethodStatementsGenerator.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,29 @@ public function getStatements(GeneratorMetadata $metadata): array
9999
}
100100

101101
if (\count($duplicatedStatements) > 0 && \count($metadata->getPropertiesInConstructor())) {
102+
/**
103+
* We know that the last statement is an `if` statement (otherwise we can't add an `else` statement).
104+
* Without this logic, the addedDependencies would only be called when the target was set. If the target is
105+
* `null` instead, the code looked like:
106+
*
107+
* ```php
108+
* if (null !== result) {
109+
* $result = // Extracted with constructor arguments
110+
* $context = \AutoMapper\MapperContext::withReference($context, $sourceHash, $result);
111+
* $context = \AutoMapper\MapperContext::withIncrementedDepth($context);
112+
* } else {
113+
* $context = \AutoMapper\MapperContext::withReference($context, $sourceHash, $result);
114+
* $context = \AutoMapper\MapperContext::withIncrementedDepth($context);
115+
*
116+
* $source->propertyName = $this->extractCallbacks['propertyName']($source);
117+
* }
118+
*/
119+
$lastStatement = $statements[array_key_last($statements)];
120+
assert($lastStatement instanceof Stmt\If_);
121+
$lastStatement->stmts = [
122+
...$lastStatement->stmts,
123+
...$addedDependenciesStatements,
124+
];
102125
/*
103126
* Generate else statements when the result is already an object, which means it has already been created,
104127
* so we need to execute the statements that need to be executed before the constructor since the constructor has already been called

tests/AutoMapperTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
use AutoMapper\Tests\Fixtures\AddressDTOWithReadonlyPromotedProperty;
2121
use AutoMapper\Tests\Fixtures\AddressType;
2222
use AutoMapper\Tests\Fixtures\AddressWithEnum;
23+
use AutoMapper\Tests\Fixtures\Category;
24+
use AutoMapper\Tests\Fixtures\CategoryDTO;
2325
use AutoMapper\Tests\Fixtures\ClassWithMapToContextAttribute;
2426
use AutoMapper\Tests\Fixtures\ClassWithNullablePropertyInConstructor;
2527
use AutoMapper\Tests\Fixtures\ClassWithPrivateProperty;
@@ -40,6 +42,7 @@
4042
use AutoMapper\Tests\Fixtures\Order;
4143
use AutoMapper\Tests\Fixtures\PetOwner;
4244
use AutoMapper\Tests\Fixtures\PetOwnerWithConstructorArguments;
45+
use AutoMapper\Tests\Fixtures\Post;
4346
use AutoMapper\Tests\Fixtures\SourceForConstructorWithDefaultValues;
4447
use AutoMapper\Tests\Fixtures\Transformer\MoneyTransformerFactory;
4548
use AutoMapper\Tests\Fixtures\Uninitialized;
@@ -1376,6 +1379,19 @@ public function testAutoMapperFixtures(string $mapFile, string $directory): void
13761379
}
13771380
}
13781381

1382+
public function testCircularReferenceForPromotedProperties(): void
1383+
{
1384+
$category = new Category('Category');
1385+
$category->posts[] = new Post('Example', $category);
1386+
1387+
$categoryDTO = $this->autoMapper->map($category, CategoryDTO::class);
1388+
1389+
self::assertEquals('Category', $categoryDTO->name);
1390+
self::assertCount(1, $categoryDTO->posts);
1391+
self::assertEquals('Example', $categoryDTO->posts[0]->name);
1392+
self::assertEquals($categoryDTO, $categoryDTO->posts[0]->category);
1393+
}
1394+
13791395
public static function provideAutoMapperFixturesTests(): iterable
13801396
{
13811397
$directories = (new Finder())

tests/Fixtures/Category.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace AutoMapper\Tests\Fixtures;
6+
7+
class Category
8+
{
9+
/** @var Post[] */
10+
public array $posts = [];
11+
12+
public function __construct(public string $name)
13+
{
14+
}
15+
}

tests/Fixtures/CategoryDTO.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace AutoMapper\Tests\Fixtures;
6+
7+
class CategoryDTO
8+
{
9+
/**
10+
* @var PostDTO[]
11+
*/
12+
public array $posts = [];
13+
14+
public function __construct(
15+
public string $name,
16+
)
17+
{
18+
}
19+
}

tests/Fixtures/Post.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace AutoMapper\Tests\Fixtures;
6+
7+
class Post
8+
{
9+
public function __construct(
10+
public string $name,
11+
public Category $category
12+
) {
13+
}
14+
}

tests/Fixtures/PostDTO.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace AutoMapper\Tests\Fixtures;
6+
7+
class PostDTO
8+
{
9+
public function __construct(
10+
public string $name,
11+
public CategoryDTO $category,
12+
)
13+
{
14+
}
15+
}

0 commit comments

Comments
 (0)