Skip to content

Commit

Permalink
Merge pull request #87 from jolicode/feat/map-discriminant-from-source
Browse files Browse the repository at this point in the history
feat(mapper): correctly map from an inherited class
  • Loading branch information
joelwurtz committed Mar 21, 2024
2 parents d737042 + a28ac3c commit 472649f
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 30 deletions.
6 changes: 4 additions & 2 deletions src/Generator/CreateTargetStatementsGenerator.php
Expand Up @@ -26,7 +26,8 @@
private Parser $parser;

public function __construct(
private DiscriminatorStatementsGenerator $discriminatorStatementsGenerator,
private DiscriminatorStatementsGenerator $discriminatorStatementsGeneratorSource,
private DiscriminatorStatementsGenerator $discriminatorStatementsGeneratorTarget,
private CachedReflectionStatementsGenerator $cachedReflectionStatementsGenerator,
?Parser $parser = null,
) {
Expand All @@ -49,7 +50,8 @@ public function generate(GeneratorMetadata $metadata, VariableRegistry $variable
$createObjectStatements[] = $this->targetAsArray($metadata);
$createObjectStatements[] = $this->sourceAndTargetAsStdClass($metadata);
$createObjectStatements[] = $this->targetAsStdClass($metadata);
$createObjectStatements = [...$createObjectStatements, ...$this->discriminatorStatementsGenerator->createTargetStatements($metadata)];
$createObjectStatements = [...$createObjectStatements, ...$this->discriminatorStatementsGeneratorSource->createTargetStatements($metadata)];
$createObjectStatements = [...$createObjectStatements, ...$this->discriminatorStatementsGeneratorTarget->createTargetStatements($metadata)];
$createObjectStatements = [...$createObjectStatements, ...$this->constructorArguments($metadata)];
$createObjectStatements[] = $this->cachedReflectionStatementsGenerator->createTargetStatement($metadata);
$createObjectStatements[] = $this->constructorWithoutArgument($metadata);
Expand Down
6 changes: 4 additions & 2 deletions src/Generator/InjectMapperMethodStatementsGenerator.php
Expand Up @@ -30,7 +30,8 @@
final readonly class InjectMapperMethodStatementsGenerator
{
public function __construct(
private DiscriminatorStatementsGenerator $discriminatorStatementsGenerator
private DiscriminatorStatementsGenerator $discriminatorStatementsGeneratorSource,
private DiscriminatorStatementsGenerator $discriminatorStatementsGeneratorTarget,
) {
}

Expand Down Expand Up @@ -65,7 +66,8 @@ public function getStatements(Expr\Variable $automapperRegistryVariable, Generat

return [
...$injectMapperStatements,
...$this->discriminatorStatementsGenerator->injectMapperStatements($metadata),
...$this->discriminatorStatementsGeneratorSource->injectMapperStatements($metadata),
...$this->discriminatorStatementsGeneratorTarget->injectMapperStatements($metadata),
];
}
}
6 changes: 4 additions & 2 deletions src/Generator/MapMethodStatementsGenerator.php
Expand Up @@ -26,13 +26,15 @@
private PropertyStatementsGenerator $propertyStatementsGenerator;

public function __construct(
DiscriminatorStatementsGenerator $discriminatorStatementsGenerator,
DiscriminatorStatementsGenerator $discriminatorStatementsGeneratorSource,
DiscriminatorStatementsGenerator $discriminatorStatementsGeneratorTarget,
CachedReflectionStatementsGenerator $cachedReflectionStatementsGenerator,
ExpressionLanguage $expressionLanguage,
private bool $allowReadOnlyTargetToPopulate = false,
) {
$this->createObjectStatementsGenerator = new CreateTargetStatementsGenerator(
$discriminatorStatementsGenerator,
$discriminatorStatementsGeneratorSource,
$discriminatorStatementsGeneratorTarget,
$cachedReflectionStatementsGenerator,
);
$this->propertyStatementsGenerator = new PropertyStatementsGenerator($expressionLanguage);
Expand Down
6 changes: 4 additions & 2 deletions src/Generator/MapperGenerator.php
Expand Up @@ -44,14 +44,16 @@ public function __construct(
);

$this->mapMethodStatementsGenerator = new MapMethodStatementsGenerator(
$discriminatorStatementsGenerator = new DiscriminatorStatementsGenerator($classDiscriminatorResolver),
$discriminatorStatementsGeneratorSource = new DiscriminatorStatementsGenerator($classDiscriminatorResolver, true),
$discriminatorStatementsGeneratorTarget = new DiscriminatorStatementsGenerator($classDiscriminatorResolver, false),
$cachedReflectionStatementsGenerator,
$expressionLanguage,
$configuration->allowReadOnlyTargetToPopulate,
);

$this->injectMapperMethodStatementsGenerator = new InjectMapperMethodStatementsGenerator(
$discriminatorStatementsGenerator
$discriminatorStatementsGeneratorSource,
$discriminatorStatementsGeneratorTarget
);

$this->disableGeneratedMapper = !$configuration->autoRegister;
Expand Down
28 changes: 14 additions & 14 deletions src/Generator/Shared/ClassDiscriminatorResolver.php
Expand Up @@ -20,10 +20,10 @@ public function __construct(
) {
}

public function hasClassDiscriminator(GeneratorMetadata $metadata): bool
public function hasClassDiscriminator(GeneratorMetadata $metadata, bool $fromSource): bool
{
if (!$metadata->isTargetUserDefined()
|| !($propertyMetadata = $this->getDiscriminatorPropertyMetadata($metadata))
if (!($fromSource ? $metadata->isSourceUserDefined() : $metadata->isTargetUserDefined())
|| !($propertyMetadata = $this->getDiscriminatorPropertyMetadata($metadata, $fromSource))
|| !$propertyMetadata->transformer instanceof TransformerInterface
) {
return false;
Expand All @@ -32,16 +32,16 @@ public function hasClassDiscriminator(GeneratorMetadata $metadata): bool
return true;
}

public function getDiscriminatorPropertyMetadata(GeneratorMetadata $metadata): ?PropertyMetadata
public function getDiscriminatorPropertyMetadata(GeneratorMetadata $metadata, bool $fromSource): ?PropertyMetadata
{
$classDiscriminatorMapping = $this->classDiscriminator?->getMappingForClass($metadata->mapperMetadata->target);
$classDiscriminatorMapping = $this->classDiscriminator?->getMappingForClass($fromSource ? $metadata->mapperMetadata->source : $metadata->mapperMetadata->target);

if (!$classDiscriminatorMapping) {
return null;
}

foreach ($metadata->propertiesMetadata as $propertyMetadata) {
if ($propertyMetadata->target->name === $classDiscriminatorMapping->getTypeProperty()) {
if (($fromSource ? $propertyMetadata->source->name : $propertyMetadata->target->name) === $classDiscriminatorMapping->getTypeProperty()) {
return $propertyMetadata;
}
}
Expand All @@ -52,44 +52,44 @@ public function getDiscriminatorPropertyMetadata(GeneratorMetadata $metadata): ?
/**
* @return array<string, string>
*/
public function discriminatorMapperNames(GeneratorMetadata $metadata): array
public function discriminatorMapperNames(GeneratorMetadata $metadata, bool $fromSource): array
{
$classDiscriminatorMapping = $this->classDiscriminator?->getMappingForClass($metadata->mapperMetadata->target);
$classDiscriminatorMapping = $this->classDiscriminator?->getMappingForClass($fromSource ? $metadata->mapperMetadata->source : $metadata->mapperMetadata->target);

if (!$classDiscriminatorMapping) {
return [];
}

return array_combine(
array_values($classDiscriminatorMapping->getTypesMapping()),
$this->discriminatorNames($metadata, $classDiscriminatorMapping)
$this->discriminatorNames($metadata, $classDiscriminatorMapping, $fromSource)
);
}

/**
* @return array<string, string>
*/
public function discriminatorMapperNamesIndexedByTypeValue(GeneratorMetadata $metadata): array
public function discriminatorMapperNamesIndexedByTypeValue(GeneratorMetadata $metadata, bool $fromSource): array
{
$classDiscriminatorMapping = $this->classDiscriminator?->getMappingForClass($metadata->mapperMetadata->target);
$classDiscriminatorMapping = $this->classDiscriminator?->getMappingForClass($fromSource ? $metadata->mapperMetadata->source : $metadata->mapperMetadata->target);

if (!$classDiscriminatorMapping) {
return [];
}

return array_combine(
array_keys($classDiscriminatorMapping->getTypesMapping()),
$this->discriminatorNames($metadata, $classDiscriminatorMapping)
$this->discriminatorNames($metadata, $classDiscriminatorMapping, $fromSource)
);
}

/**
* @return list<string>
*/
private function discriminatorNames(GeneratorMetadata $metadata, ClassDiscriminatorMapping $classDiscriminatorMapping): array
private function discriminatorNames(GeneratorMetadata $metadata, ClassDiscriminatorMapping $classDiscriminatorMapping, bool $fromSource): array
{
return array_map(
static fn (string $typeTarget) => "Discriminator_Mapper_{$metadata->mapperMetadata->source}_{$typeTarget}",
static fn (string $typeTarget) => $fromSource ? "Discriminator_Mapper_{$typeTarget}_{$metadata->mapperMetadata->target}" : "Discriminator_Mapper_{$metadata->mapperMetadata->source}_{$typeTarget}",
$classDiscriminatorMapping->getTypesMapping()
);
}
Expand Down
15 changes: 8 additions & 7 deletions src/Generator/Shared/DiscriminatorStatementsGenerator.php
Expand Up @@ -23,6 +23,7 @@
{
public function __construct(
private ClassDiscriminatorResolver $classDiscriminatorResolver,
private bool $fromSource,
) {
}

Expand All @@ -35,7 +36,7 @@ public function injectMapperStatements(GeneratorMetadata $metadata): array
return [];
}

$discriminatorMapperNames = $this->classDiscriminatorResolver->discriminatorMapperNames($metadata);
$discriminatorMapperNames = $this->classDiscriminatorResolver->discriminatorMapperNames($metadata, $this->fromSource);

$injectMapperStatements = [];

Expand All @@ -56,8 +57,8 @@ public function injectMapperStatements(GeneratorMetadata $metadata): array
new Scalar\String_($discriminatorMapperName)
),
new Expr\MethodCall(new Expr\Variable('autoMapperRegistry'), 'getMapper', [
new Arg(new Scalar\String_($metadata->mapperMetadata->source)),
new Arg(new Scalar\String_($typeTarget)),
new Arg(new Scalar\String_($this->fromSource ? $typeTarget : $metadata->mapperMetadata->source)),
new Arg(new Scalar\String_($this->fromSource ? $metadata->mapperMetadata->target : $typeTarget)),
])
)
);
Expand All @@ -83,7 +84,7 @@ public function createTargetStatements(GeneratorMetadata $metadata): array
return [];
}

$propertyMetadata = $this->classDiscriminatorResolver->getDiscriminatorPropertyMetadata($metadata);
$propertyMetadata = $this->classDiscriminatorResolver->getDiscriminatorPropertyMetadata($metadata, $this->fromSource);

if (!$propertyMetadata) {
return [];
Expand All @@ -110,7 +111,7 @@ public function createTargetStatements(GeneratorMetadata $metadata): array
$variableRegistry->getSourceInput()
);

foreach ($this->classDiscriminatorResolver->discriminatorMapperNamesIndexedByTypeValue($metadata) as $typeValue => $discriminatorMapperName) {
foreach ($this->classDiscriminatorResolver->discriminatorMapperNamesIndexedByTypeValue($metadata, $this->fromSource) as $typeValue => $discriminatorMapperName) {
$createObjectStatements[] = new Stmt\If_(
new Expr\BinaryOp\Identical(new Scalar\String_($typeValue), $output),
[
Expand Down Expand Up @@ -138,11 +139,11 @@ public function createTargetStatements(GeneratorMetadata $metadata): array

private function supports(GeneratorMetadata $metadata): bool
{
if (!$this->classDiscriminatorResolver->hasClassDiscriminator($metadata)) {
if (!$this->classDiscriminatorResolver->hasClassDiscriminator($metadata, $this->fromSource)) {
return false;
}

$propertyMetadata = $this->classDiscriminatorResolver->getDiscriminatorPropertyMetadata($metadata);
$propertyMetadata = $this->classDiscriminatorResolver->getDiscriminatorPropertyMetadata($metadata, $this->fromSource);

return $propertyMetadata && $propertyMetadata->transformer instanceof TransformerInterface;
}
Expand Down
9 changes: 9 additions & 0 deletions src/Metadata/GeneratorMetadata.php
Expand Up @@ -56,6 +56,15 @@ public function isTargetUserDefined(): bool
return $this->mapperMetadata->targetReflectionClass->isUserDefined();
}

public function isSourceUserDefined(): bool
{
if (null === $this->mapperMetadata->sourceReflectionClass) {
return false;
}

return $this->mapperMetadata->sourceReflectionClass->isUserDefined();
}

public function hasConstructor(): bool
{
if (!$this->allowConstructor) {
Expand Down
27 changes: 26 additions & 1 deletion tests/AutoMapperTest.php
Expand Up @@ -22,6 +22,7 @@
use AutoMapper\Tests\Fixtures\ClassWithNullablePropertyInConstructor;
use AutoMapper\Tests\Fixtures\ClassWithPrivateProperty;
use AutoMapper\Tests\Fixtures\DifferentSetterGetterType;
use AutoMapper\Tests\Fixtures\Dog;
use AutoMapper\Tests\Fixtures\Fish;
use AutoMapper\Tests\Fixtures\FooGenerator;
use AutoMapper\Tests\Fixtures\HasDateTime;
Expand Down Expand Up @@ -927,7 +928,7 @@ public function testAdderAndRemoverWithClass(): void
$petOwner = [
'pets' => [
['type' => 'cat', 'name' => 'Félix'],
['type' => 'dog', 'name' => 'Coco'],
['type' => 'dog', 'name' => 'Coco', 'bark' => 'Wouf'],
],
];

Expand All @@ -939,6 +940,7 @@ public function testAdderAndRemoverWithClass(): void
self::assertSame('cat', $petOwnerData->getPets()[0]->type);
self::assertSame('Coco', $petOwnerData->getPets()[1]->name);
self::assertSame('dog', $petOwnerData->getPets()[1]->type);
self::assertSame('Wouf', $petOwnerData->getPets()[1]->bark);
}

public function testAdderAndRemoverWithInstance(): void
Expand Down Expand Up @@ -1389,4 +1391,27 @@ public function testRealClassName(): void
self::assertNotEquals('Mapper_MongoDBODMProxies___PM___AutoMapper_Tests_Fixtures_Proxy_Generated_array', $mapper::class);
self::assertEquals('Mapper_AutoMapper_Tests_Fixtures_Proxy_array', $mapper::class);
}

public function testDiscriminantToArray(): void
{
$this->buildAutoMapper(mapPrivatePropertiesAndMethod: true);

$dog = new Dog();
$dog->bark = 'Wouf';
$dog->type = 'dog';
$dog->name = 'Coco';

$petOwner = new PetOwner();
$petOwner->addPet($dog);

$dog->owner = $petOwner;

$petOwnerData = $this->autoMapper->map($petOwner, 'array');

self::assertIsArray($petOwnerData['pets']);
self::assertCount(1, $petOwnerData['pets']);
self::assertSame('Coco', $petOwnerData['pets'][0]['name']);
self::assertSame('dog', $petOwnerData['pets'][0]['type']);
self::assertSame('Wouf', $petOwnerData['pets'][0]['bark']);
}
}
1 change: 1 addition & 0 deletions tests/Fixtures/Dog.php
Expand Up @@ -6,4 +6,5 @@

class Dog extends Pet
{
public string $bark = 'Woof! Woof!';
}

0 comments on commit 472649f

Please sign in to comment.