Skip to content

Commit

Permalink
Merge pull request #75 from jolicode/fix/multiple-types
Browse files Browse the repository at this point in the history
Types: better matching between types to better handle multiple types
  • Loading branch information
joelwurtz committed Mar 19, 2024
2 parents 9cfbc0b + cc51124 commit a97a293
Show file tree
Hide file tree
Showing 45 changed files with 523 additions and 357 deletions.
2 changes: 2 additions & 0 deletions src/Event/PropertyMetadataEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace AutoMapper\Event;

use AutoMapper\Metadata\MapperMetadata;
use AutoMapper\Metadata\TypesMatching;
use AutoMapper\Transformer\TransformerInterface;

/**
Expand All @@ -16,6 +17,7 @@ public function __construct(
public readonly MapperMetadata $mapperMetadata,
public readonly SourcePropertyMetadata $source,
public readonly TargetPropertyMetadata $target,
public ?TypesMatching $types = null,
public ?int $maxDepth = null,
public ?TransformerInterface $transformer = null,
public ?bool $ignored = null,
Expand Down
12 changes: 6 additions & 6 deletions src/EventListener/MapFromListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ private function addPropertyFromTarget(GenerateMapperEvent $event, MapFrom $mapF
$targetProperty = new TargetPropertyMetadata($name);

$property = new PropertyMetadataEvent(
$event->mapperMetadata,
$sourceProperty,
$targetProperty,
$mapFrom->maxDepth,
$this->getTransformerFromMapAttribute($event->mapperMetadata->target, $mapFrom),
$mapFrom->ignore,
mapperMetadata: $event->mapperMetadata,
source: $sourceProperty,
target: $targetProperty,
maxDepth: $mapFrom->maxDepth,
transformer: $this->getTransformerFromMapAttribute($event->mapperMetadata->target, $mapFrom),
ignored: $mapFrom->ignore,
);

if (\array_key_exists($property->target->name, $event->properties)) {
Expand Down
12 changes: 6 additions & 6 deletions src/EventListener/MapToListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ private function addPropertyFromSource(GenerateMapperEvent $event, MapTo $mapTo,
$targetProperty = new TargetPropertyMetadata($mapTo->name ?? $name);

$property = new PropertyMetadataEvent(
$event->mapperMetadata,
$sourceProperty,
$targetProperty,
$mapTo->maxDepth,
$this->getTransformerFromMapAttribute($event->mapperMetadata->source, $mapTo),
$mapTo->ignore,
mapperMetadata: $event->mapperMetadata,
source: $sourceProperty,
target: $targetProperty,
maxDepth: $mapTo->maxDepth,
transformer: $this->getTransformerFromMapAttribute($event->mapperMetadata->source, $mapTo),
ignored: $mapTo->ignore,
);

if (\array_key_exists($property->target->name, $event->properties)) {
Expand Down
9 changes: 5 additions & 4 deletions src/Extractor/FromSourceMappingExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace AutoMapper\Extractor;

use AutoMapper\Configuration;
use AutoMapper\Metadata\TypesMatching;
use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface;
use Symfony\Component\PropertyInfo\PropertyReadInfoExtractorInterface;
use Symfony\Component\PropertyInfo\PropertyWriteInfoExtractorInterface;
Expand All @@ -30,20 +31,20 @@ public function __construct(
parent::__construct($configuration, $propertyInfoExtractor, $readInfoExtractor, $writeInfoExtractor);
}

public function getTypes(string $source, string $sourceProperty, string $target, string $targetProperty): array
public function getTypes(string $source, string $sourceProperty, string $target, string $targetProperty): TypesMatching
{
$types = new TypesMatching();
$sourceTypes = $this->propertyInfoExtractor->getTypes($source, $sourceProperty) ?? [new Type(Type::BUILTIN_TYPE_NULL)];
$targetTypes = [];

foreach ($sourceTypes as $type) {
$targetType = $this->transformType($target, $type);

if ($targetType) {
$targetTypes[] = $targetType;
$types[$type] = [$targetType];
}
}

return [$sourceTypes, $targetTypes];
return $types;
}

private function transformType(string $target, Type $type = null): ?Type
Expand Down
9 changes: 5 additions & 4 deletions src/Extractor/FromTargetMappingExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace AutoMapper\Extractor;

use AutoMapper\Configuration;
use AutoMapper\Metadata\TypesMatching;
use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface;
use Symfony\Component\PropertyInfo\PropertyReadInfoExtractorInterface;
use Symfony\Component\PropertyInfo\PropertyWriteInfoExtractorInterface;
Expand All @@ -30,20 +31,20 @@ public function __construct(
parent::__construct($configuration, $propertyInfoExtractor, $readInfoExtractor, $writeInfoExtractor);
}

public function getTypes(string $source, string $sourceProperty, string $target, string $targetProperty): array
public function getTypes(string $source, string $sourceProperty, string $target, string $targetProperty): TypesMatching
{
$types = new TypesMatching();
$targetTypes = $this->propertyInfoExtractor->getTypes($target, $targetProperty) ?? [];
$sourceTypes = [];

foreach ($targetTypes as $type) {
$sourceType = $this->transformType($source, $type);

if ($sourceType) {
$sourceTypes[] = $sourceType;
$types[$sourceType] = [$type];
}
}

return [$sourceTypes, $targetTypes];
return $types;
}

public function getReadAccessor(string $source, string $target, string $property): ?ReadAccessor
Expand Down
7 changes: 2 additions & 5 deletions src/Extractor/MappingExtractorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace AutoMapper\Extractor;

use Symfony\Component\PropertyInfo\Type;
use AutoMapper\Metadata\TypesMatching;

/**
* Extracts mapping.
Expand All @@ -22,10 +22,7 @@ interface MappingExtractorInterface
*/
public function getProperties(string $class): iterable;

/**
* @return array{0: Type[], 1: Type[]}
*/
public function getTypes(string $source, string $sourceProperty, string $target, string $targetProperty): array;
public function getTypes(string $source, string $sourceProperty, string $target, string $targetProperty): TypesMatching;

public function getDateTimeFormat(string $class, string $property): string;

Expand Down
6 changes: 4 additions & 2 deletions src/Extractor/SourceTargetMappingExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace AutoMapper\Extractor;

use AutoMapper\Metadata\TypesMatching;

/**
* Extracts mapping between two objects, only gives properties that have the same name.
*
Expand All @@ -13,11 +15,11 @@
*/
class SourceTargetMappingExtractor extends MappingExtractor
{
public function getTypes(string $source, string $sourceProperty, string $target, string $targetProperty): array
public function getTypes(string $source, string $sourceProperty, string $target, string $targetProperty): TypesMatching
{
$sourceTypes = $this->propertyInfoExtractor->getTypes($source, $sourceProperty) ?? [];
$targetTypes = $this->propertyInfoExtractor->getTypes($target, $targetProperty) ?? [];

return [$sourceTypes, $targetTypes];
return TypesMatching::fromSourceAndTargetTypes($sourceTypes, $targetTypes);
}
}
17 changes: 6 additions & 11 deletions src/Metadata/MetadataRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,13 +152,7 @@ private function createGeneratorMetadata(MapperMetadata $mapperMetadata): Genera
$propertiesMapping = [];

foreach ($propertyEvents as $propertyMappedEvent) {
[$sourceTypes, $targetTypes] = $extractor->getTypes($mapperMetadata->source, $propertyMappedEvent->source->name, $mapperMetadata->target, $propertyMappedEvent->target->name);

// Create the source property metadata
if ($propertyMappedEvent->source->types === null) {
$propertyMappedEvent->source->types = $sourceTypes;
}

if ($propertyMappedEvent->source->accessor === null) {
$propertyMappedEvent->source->accessor = $extractor->getReadAccessor($mapperMetadata->source, $mapperMetadata->target, $propertyMappedEvent->source->name);
}
Expand All @@ -176,10 +170,6 @@ private function createGeneratorMetadata(MapperMetadata $mapperMetadata): Genera
}

// Create the target property metadata
if ($propertyMappedEvent->target->types === null) {
$propertyMappedEvent->target->types = $targetTypes;
}

if ($propertyMappedEvent->target->writeMutator === null) {
$propertyMappedEvent->target->writeMutator = $extractor->getWriteMutator($mapperMetadata->source, $mapperMetadata->target, $propertyMappedEvent->target->name, [
'enable_constructor_extraction' => false,
Expand All @@ -203,8 +193,12 @@ private function createGeneratorMetadata(MapperMetadata $mapperMetadata): Genera
$sourcePropertyMetadata = SourcePropertyMetadata::fromEvent($propertyMappedEvent->source);
$targetPropertyMetadata = TargetPropertyMetadata::fromEvent($propertyMappedEvent->target);

if (null === $propertyMappedEvent->types) {
$propertyMappedEvent->types = $extractor->getTypes($mapperMetadata->source, $propertyMappedEvent->source->name, $mapperMetadata->target, $propertyMappedEvent->target->name);
}

if (null === $propertyMappedEvent->transformer) {
$transformer = $this->transformerFactory->getTransformer($sourcePropertyMetadata, $targetPropertyMetadata, $mapperMetadata);
$transformer = $this->transformerFactory->getTransformer($propertyMappedEvent->types, $sourcePropertyMetadata, $targetPropertyMetadata, $mapperMetadata);

if (null === $transformer) {
continue;
Expand All @@ -220,6 +214,7 @@ private function createGeneratorMetadata(MapperMetadata $mapperMetadata): Genera
$propertiesMapping[] = new PropertyMetadata(
$sourcePropertyMetadata,
$targetPropertyMetadata,
$propertyMappedEvent->types,
$propertyMappedEvent->transformer,
$propertyMappedEvent->ignored,
$propertyMappedEvent->maxDepth,
Expand Down
1 change: 1 addition & 0 deletions src/Metadata/PropertyMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ final class PropertyMetadata
public function __construct(
public readonly SourcePropertyMetadata $source,
public readonly TargetPropertyMetadata $target,
public readonly TypesMatching $types,
public TransformerInterface $transformer,
public bool $isIgnored = false,
public ?int $maxDepth = null,
Expand Down
12 changes: 0 additions & 12 deletions src/Metadata/SourcePropertyMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use AutoMapper\Event\SourcePropertyMetadata as SourcePropertyMetadataEvent;
use AutoMapper\Extractor\ReadAccessor;
use Symfony\Component\PropertyInfo\Type;

/**
* Source Property metadata.
Expand All @@ -18,11 +17,9 @@
final readonly class SourcePropertyMetadata
{
/**
* @param Type[] $types
* @param string[]|null $groups
*/
public function __construct(
public array $types,
public string $name,
public ?ReadAccessor $accessor = null,
public bool $checkExists = false,
Expand All @@ -31,18 +28,9 @@ public function __construct(
) {
}

/**
* @param Type[] $types
*/
public function withTypes(array $types): self
{
return new self($types, $this->name, $this->accessor, $this->checkExists, $this->groups);
}

public static function fromEvent(SourcePropertyMetadataEvent $metadata): self
{
return new self(
$metadata->types ?? [],
$metadata->name,
$metadata->accessor,
$metadata->checkExists ?? false,
Expand Down
12 changes: 0 additions & 12 deletions src/Metadata/TargetPropertyMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use AutoMapper\Event\TargetPropertyMetadata as EventTargetPropertyMetadata;
use AutoMapper\Extractor\WriteMutator;
use Symfony\Component\PropertyInfo\Type;

/**
* Target Property metadata.
Expand All @@ -18,11 +17,9 @@
final readonly class TargetPropertyMetadata
{
/**
* @param Type[] $types
* @param string[]|null $groups
*/
public function __construct(
public array $types,
public string $name,
public ?WriteMutator $writeMutator = null,
public ?WriteMutator $writeMutatorConstructor = null,
Expand All @@ -31,18 +28,9 @@ public function __construct(
) {
}

/**
* @param Type[] $types
*/
public function withTypes(array $types): self
{
return new self($types, $this->name, $this->writeMutator, $this->writeMutatorConstructor, $this->groups, $this->dateTimeFormat);
}

public static function fromEvent(EventTargetPropertyMetadata $metadata): self
{
return new self(
$metadata->types ?? [],
$metadata->name,
$metadata->writeMutator,
$metadata->writeMutatorConstructor,
Expand Down
58 changes: 58 additions & 0 deletions src/Metadata/TypesMatching.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

namespace AutoMapper\Metadata;

use Symfony\Component\PropertyInfo\Type;

/**
* @internal
*
* @extends \SplObjectStorage<Type, Type[]>
*/
final class TypesMatching extends \SplObjectStorage
{
public function getSourceUniqueType(): ?Type
{
if (0 === \count($this) || \count($this) > 1) {
return null;
}

// Get first type from the SplObjectStorage
$this->rewind();
$sourceType = $this->current();

if (!$sourceType instanceof Type) {
return null;
}

return $sourceType;
}

public function getTargetUniqueType(Type $sourceType): ?Type
{
$targetTypes = $this[$sourceType] ?? [];

if (0 === \count($targetTypes) || \count($targetTypes) > 1 || !$targetTypes[0] instanceof Type) {
return null;
}

return $targetTypes[0];
}

/**
* @param Type[] $sourceTypes
* @param Type[] $targetTypes
*/
public static function fromSourceAndTargetTypes(array $sourceTypes, array $targetTypes): self
{
$types = new self();

foreach ($sourceTypes as $sourceType) {
$types[$sourceType] = $targetTypes;
}

return $types;
}
}
2 changes: 1 addition & 1 deletion src/Transformer/AbstractArrayTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
abstract readonly class AbstractArrayTransformer implements TransformerInterface, DependentTransformerInterface
{
public function __construct(
private TransformerInterface $itemTransformer,
protected TransformerInterface $itemTransformer,
) {
}

Expand Down
Loading

0 comments on commit a97a293

Please sign in to comment.