Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

- [GH#304](https://github.com/jolicode/automapper/pull/304) ; Allow to override source and/or target property type
- [GH#297](https://github.com/jolicode/automapper/pull/297) : Support PHP 8.5 and Symfony 8, this library now use the `TypeInfo` Component for types instead of PropertyInfo directly.
- [BC Break] `PropertyTransformerSupportInterface` does not use a `TypesMatching` anymore, you can get the type directly from `SourcePropertyMetadata` or `TargetPropertyMetadata`.
- Debug command now show the type of each property mapped, transformers will also display more information.
Expand Down
2 changes: 1 addition & 1 deletion docs/bundle/expression-language.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ The `env` function allow you to access the environment variables.
```php
class Entity
{
#[MapTo('array', if: "env('FEATURE_ENABLED')")]
#[MapTo(target: 'array', if: "env('FEATURE_ENABLED')")]
public string $name;
}
```
Expand Down
2 changes: 1 addition & 1 deletion docs/bundle/migrate.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ For example, if you have a custom normalizer that add a `virtualProperty` the no
to do the same thing.

```php
#[MapTo('array', property: 'virtualProperty', transformer: MyTransformer::class)]
#[MapTo(target: 'array', property: 'virtualProperty', transformer: MyTransformer::class)]
class App\Entity\MyEntity
{
// ...
Expand Down
1 change: 1 addition & 0 deletions docs/mapping/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ a `source` and a `target`.
- [Groups](groups.md)
- [Transformer](transformer.md)
- [Provider](provider.md)
- [Type](type.md)
- [Mapping inheritance](inheritance.md)
- [Identifier: mapping existing objects](identifier.md)
- [DateTime format](date-time.md)
34 changes: 34 additions & 0 deletions docs/mapping/type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Property Type

When mapping properties, AutoMapper uses the property type to determine how to map the value from the source to the target.

It works well when both source and target properties are object, but when mapping to or from a generic data structure
like an array or `\stdClass`, the property type is not available.

In this case the type is, by default, transformed to a native PHP type (`int`, `float`, `string`, `bool`, `array`, `object`, `null`).

You can override this behavior by specifying the `sourcePropertyType` or `targetPropertyType` argument in the
`#[MapTo]` or `#[MapFrom]` attributes.

```php
class Entity
{
#[MapTo(target: 'array', targetPropertyType: 'int']
public string $number;
}
```

In this example, when mapping to an array, the `number` property will be converted to an `int` instead of a `string`.

This can also be useful when mapping to an object type with an union type, but you want to force a specific type during the mapping.

```php
class EntityDto
{
#[MapTo(target: Entity:class, sourcePropertyType: 'int']
private int|float $value;
}
```

In this example we consider, that the source property is always an `int`, so AutoMapper will never consider
the `float` type during the mapping.
6 changes: 4 additions & 2 deletions src/Attribute/MapFrom.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
* @param string|null $dateTimeFormat The date-time format to use when transforming this property
* @param bool|null $extractTypesFromGetter If true, the types will be extracted from the getter method
* @param bool|null $identifier If true, the property will be used as an identifier
* @param Type|string|null $sourcePropertyType Override the source property type, where this property is mapped from
* @param Type|string|null $targetPropertyType Override the target property type, which in this case is the property type where the attribute is defined
*/
public function __construct(
public string|array|null $source = null,
Expand All @@ -36,8 +38,8 @@ public function __construct(
public ?string $dateTimeFormat = null,
public ?bool $extractTypesFromGetter = null,
public ?bool $identifier = null,
public ?Type $sourceType = null,
public ?Type $targetType = null,
public string|Type|null $sourcePropertyType = null,
public string|Type|null $targetPropertyType = null,
) {
}
}
6 changes: 4 additions & 2 deletions src/Attribute/MapTo.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
* @param string|null $dateTimeFormat The date-time format to use when transforming this property
* @param bool|null $extractTypesFromGetter If true, the types will be extracted from the getter method
* @param bool|null $identifier If true, the property will be used as an identifier
* @param Type|string|null $sourcePropertyType Override the source property type, which in this case is the property type where the attribute is defined
* @param Type|string|null $targetPropertyType Override the target property type where this property is mapped to
*/
public function __construct(
public string|array|null $target = null,
Expand All @@ -36,8 +38,8 @@ public function __construct(
public ?string $dateTimeFormat = null,
public ?bool $extractTypesFromGetter = null,
public ?bool $identifier = null,
public ?Type $sourceType = null,
public ?Type $targetType = null,
public string|Type|null $sourcePropertyType = null,
public string|Type|null $targetPropertyType = null,
) {
}
}
7 changes: 5 additions & 2 deletions src/EventListener/MapFromListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,11 @@ private function addPropertyFromTarget(GenerateMapperEvent $event, MapFrom $mapF
return;
}

$sourceProperty = new SourcePropertyMetadata($mapFrom->property ?? $property, type: $mapFrom->sourceType);
$targetProperty = new TargetPropertyMetadata($property, type: $mapFrom->targetType);
$sourcePropertyType = \is_string($mapFrom->sourcePropertyType) ? $this->stringTypeResolver->resolve($mapFrom->sourcePropertyType) : $mapFrom->sourcePropertyType;
$targetPropertyType = \is_string($mapFrom->targetPropertyType) ? $this->stringTypeResolver->resolve($mapFrom->targetPropertyType) : $mapFrom->targetPropertyType;

$sourceProperty = new SourcePropertyMetadata($mapFrom->property ?? $property, type: $sourcePropertyType);
$targetProperty = new TargetPropertyMetadata($property, type: $targetPropertyType);

$propertyMetadata = new PropertyMetadataEvent(
mapperMetadata: $event->mapperMetadata,
Expand Down
2 changes: 2 additions & 0 deletions src/EventListener/MapListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
use Symfony\Component\String\Inflector\EnglishInflector;
use Symfony\Component\String\Inflector\InflectorInterface;
use Symfony\Component\TypeInfo\TypeResolver\StringTypeResolver;

/**
* @internal
Expand All @@ -28,6 +29,7 @@ public function __construct(
private PropertyTransformerRegistry $propertyTransformerRegistry,
private ExpressionLanguage $expressionLanguage,
private InflectorInterface $inflector = new EnglishInflector(),
protected StringTypeResolver $stringTypeResolver = new StringTypeResolver(),
) {
}

Expand Down
7 changes: 5 additions & 2 deletions src/EventListener/MapToListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,11 @@ private function addPropertyFromSource(GenerateMapperEvent $event, MapTo $mapTo,
return;
}

$sourceProperty = new SourcePropertyMetadata($property, type: $mapTo->sourceType);
$targetProperty = new TargetPropertyMetadata($mapTo->property ?? $property, type: $mapTo->targetType);
$sourcePropertyType = \is_string($mapTo->sourcePropertyType) ? $this->stringTypeResolver->resolve($mapTo->sourcePropertyType) : $mapTo->sourcePropertyType;
$targetPropertyType = \is_string($mapTo->targetPropertyType) ? $this->stringTypeResolver->resolve($mapTo->targetPropertyType) : $mapTo->targetPropertyType;

$sourceProperty = new SourcePropertyMetadata($property, type: $sourcePropertyType);
$targetProperty = new TargetPropertyMetadata($mapTo->property ?? $property, type: $targetPropertyType);

$propertyMetadata = new PropertyMetadataEvent(
mapperMetadata: $event->mapperMetadata,
Expand Down
2 changes: 2 additions & 0 deletions tests/AutoMapperMapToTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public function testMapToArray()
$this->assertSame('transformed', $bar['transformFromExpressionLanguage']);
$this->assertSame('bar', $bar['transformWithExpressionFunction']);
$this->assertSame(0, $bar['fooInt']);
$this->assertSame(0.0, $bar['fooFloat']);

$foo = new FooMapTo('bar');
$bar = $this->autoMapper->map($foo, 'array');
Expand All @@ -80,6 +81,7 @@ public function testMapToArray()
$this->assertSame('bar', $bar['transformFromCustomTransformerService']);
$this->assertSame('not transformed', $bar['transformFromExpressionLanguage']);
$this->assertSame(0, $bar['fooInt']);
$this->assertSame(0.0, $bar['fooFloat']);
}

public function testMapToArrayGroups()
Expand Down
3 changes: 2 additions & 1 deletion tests/Fixtures/MapTo/FooMapTo.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ public function __construct(
#[MapTo('array', property: 'transformFromCustomTransformerService', transformer: TransformerWithDependency::class)]
#[MapTo('array', property: 'transformFromExpressionLanguage', transformer: "source.foo === 'foo' ? 'transformed' : 'not transformed'")]
#[MapTo('array', property: 'foo')]
#[MapTo('array', property: 'fooInt', targetType: new Type\BuiltinType(TypeIdentifier::INT))]
#[MapTo('array', property: 'fooInt', targetPropertyType: new Type\BuiltinType(TypeIdentifier::INT))]
#[MapTo('array', property: 'fooFloat', targetPropertyType: 'float')]
public string $foo,
) {
}
Expand Down