diff --git a/src/Generator/MapMethodStatementsGenerator.php b/src/Generator/MapMethodStatementsGenerator.php index 2f3f517..5fbe075 100644 --- a/src/Generator/MapMethodStatementsGenerator.php +++ b/src/Generator/MapMethodStatementsGenerator.php @@ -99,6 +99,29 @@ public function getStatements(GeneratorMetadata $metadata): array } if (\count($duplicatedStatements) > 0 && \count($metadata->getPropertiesInConstructor())) { + /** + * We know that the last statement is an `if` statement (otherwise we can't add an `else` statement). + * Without this logic, the addedDependencies would only be called when the target was set. If the target is + * `null` instead, the code looked like: + * + * ```php + * if (null !== result) { + * $result = // Extracted with constructor arguments + * $context = \AutoMapper\MapperContext::withReference($context, $sourceHash, $result); + * $context = \AutoMapper\MapperContext::withIncrementedDepth($context); + * } else { + * $context = \AutoMapper\MapperContext::withReference($context, $sourceHash, $result); + * $context = \AutoMapper\MapperContext::withIncrementedDepth($context); + * + * $source->propertyName = $this->extractCallbacks['propertyName']($source); + * } + */ + $lastStatement = $statements[array_key_last($statements)]; + assert($lastStatement instanceof Stmt\If_); + $lastStatement->stmts = [ + ...$lastStatement->stmts, + ...$addedDependenciesStatements, + ]; /* * Generate else statements when the result is already an object, which means it has already been created, * so we need to execute the statements that need to be executed before the constructor since the constructor has already been called diff --git a/tests/AutoMapperTest.php b/tests/AutoMapperTest.php index b90385b..957beae 100644 --- a/tests/AutoMapperTest.php +++ b/tests/AutoMapperTest.php @@ -20,6 +20,8 @@ use AutoMapper\Tests\Fixtures\AddressDTOWithReadonlyPromotedProperty; use AutoMapper\Tests\Fixtures\AddressType; use AutoMapper\Tests\Fixtures\AddressWithEnum; +use AutoMapper\Tests\Fixtures\Category; +use AutoMapper\Tests\Fixtures\CategoryDTO; use AutoMapper\Tests\Fixtures\ClassWithMapToContextAttribute; use AutoMapper\Tests\Fixtures\ClassWithNullablePropertyInConstructor; use AutoMapper\Tests\Fixtures\ClassWithPrivateProperty; @@ -40,6 +42,7 @@ use AutoMapper\Tests\Fixtures\Order; use AutoMapper\Tests\Fixtures\PetOwner; use AutoMapper\Tests\Fixtures\PetOwnerWithConstructorArguments; +use AutoMapper\Tests\Fixtures\Post; use AutoMapper\Tests\Fixtures\SourceForConstructorWithDefaultValues; use AutoMapper\Tests\Fixtures\Transformer\MoneyTransformerFactory; use AutoMapper\Tests\Fixtures\Uninitialized; @@ -1376,6 +1379,19 @@ public function testAutoMapperFixtures(string $mapFile, string $directory): void } } + public function testCircularReferenceForPromotedProperties(): void + { + $category = new Category('Category'); + $category->posts[] = new Post('Example', $category); + + $categoryDTO = $this->autoMapper->map($category, CategoryDTO::class); + + self::assertEquals('Category', $categoryDTO->name); + self::assertCount(1, $categoryDTO->posts); + self::assertEquals('Example', $categoryDTO->posts[0]->name); + self::assertEquals($categoryDTO, $categoryDTO->posts[0]->category); + } + public static function provideAutoMapperFixturesTests(): iterable { $directories = (new Finder()) diff --git a/tests/Fixtures/Category.php b/tests/Fixtures/Category.php new file mode 100644 index 0000000..34e8cc9 --- /dev/null +++ b/tests/Fixtures/Category.php @@ -0,0 +1,15 @@ +