Skip to content

Commit 3fd739d

Browse files
committed
feat(types): allow to extract from getter on target
1 parent 85459bb commit 3fd739d

15 files changed

+125
-32
lines changed

castor.php

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,22 @@ function debug_mapper(string $source, string $target, string $load = '')
3939
{
4040
require_once __DIR__ . '/vendor/autoload.php';
4141

42-
if ($load) {
43-
require_once $load;
44-
}
42+
// special autoloader for "AutoMapperTests"
43+
spl_autoload_register(function (string $class) {
44+
if (!str_starts_with($class, 'AutoMapper\\Tests\\AutoMapperTest\\')) {
45+
return false;
46+
}
47+
48+
// split on namespace separator
49+
$parts = explode('\\', $class);
50+
// get second part
51+
$testDirectory = $parts[3] ?? '';
52+
$mapFile = __DIR__ . '/tests/AutoMapperTest/' . $testDirectory . '/map.php';
53+
54+
if (file_exists($mapFile)) {
55+
require_once $mapFile;
56+
}
57+
});
4558

4659
$automapper = AutoMapper\AutoMapper::create();
4760
// get private property loader value

src/Attribute/MapFrom.php

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@
1111
final readonly class MapFrom
1212
{
1313
/**
14-
* @param class-string<object>|'array'|array<class-string<object>|'array'>|null $source The specific source class name or array. If null this attribute will be used for all source classes.
15-
* @param string|null $property The source property name. If null, the target property name will be used.
16-
* @param int|null $maxDepth The maximum depth of the mapping. If null, the default max depth will be used.
17-
* @param string|callable(mixed $value, object|array<string, mixed> $source, array<string, mixed> $context): mixed $transformer A transformer id or a callable that transform the value during mapping
18-
* @param bool|null $ignore If true, the property will be ignored during mapping
19-
* @param string|null $if The condition to map the property, using the expression language
20-
* @param string[]|null $groups The groups to map the property
21-
* @param string|null $dateTimeFormat The date-time format to use when transforming this property
14+
* @param class-string<object>|'array'|array<class-string<object>|'array'>|null $source The specific source class name or array. If null this attribute will be used for all source classes.
15+
* @param string|null $property The source property name. If null, the target property name will be used.
16+
* @param int|null $maxDepth The maximum depth of the mapping. If null, the default max depth will be used.
17+
* @param string|callable(mixed $value, object|array<string, mixed> $source, array<string, mixed> $context): mixed $transformer A transformer id or a callable that transform the value during mapping
18+
* @param bool|null $ignore If true, the property will be ignored during mapping
19+
* @param string|null $if The condition to map the property, using the expression language
20+
* @param string[]|null $groups The groups to map the property
21+
* @param string|null $dateTimeFormat The date-time format to use when transforming this property
22+
* @param bool|null $extractTypesFromGetter If true, the types will be extracted from the getter method
2223
*/
2324
public function __construct(
2425
public string|array|null $source = null,
@@ -30,6 +31,7 @@ public function __construct(
3031
public ?array $groups = null,
3132
public int $priority = 0,
3233
public ?string $dateTimeFormat = null,
34+
public ?bool $extractTypesFromGetter = null,
3335
) {
3436
}
3537
}

src/Attribute/MapTo.php

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@
1111
final readonly class MapTo
1212
{
1313
/**
14-
* @param class-string<object>|'array'|array<class-string<object>|'array'>|null $target The specific target class name or array. If null this attribute will be used for all target classes.
15-
* @param string|null $property The target property name. If null, the source property name will be used.
16-
* @param int|null $maxDepth The maximum depth of the mapping. If null, the default max depth will be used.
17-
* @param string|callable(mixed $value, object|array<string, mixed> $source, array<string, mixed> $context): mixed $transformer A transformer id or a callable that transform the value during mapping
18-
* @param bool|null $ignore If true, the property will be ignored during mapping
19-
* @param string|null $if The condition to map the property, using the expression language
20-
* @param string[]|null $groups The groups to map the property
21-
* @param string|null $dateTimeFormat The date-time format to use when transforming this property
14+
* @param class-string<object>|'array'|array<class-string<object>|'array'>|null $target The specific target class name or array. If null this attribute will be used for all target classes.
15+
* @param string|null $property The target property name. If null, the source property name will be used.
16+
* @param int|null $maxDepth The maximum depth of the mapping. If null, the default max depth will be used.
17+
* @param string|callable(mixed $value, object|array<string, mixed> $source, array<string, mixed> $context): mixed $transformer A transformer id or a callable that transform the value during mapping
18+
* @param bool|null $ignore If true, the property will be ignored during mapping
19+
* @param string|null $if The condition to map the property, using the expression language
20+
* @param string[]|null $groups The groups to map the property
21+
* @param string|null $dateTimeFormat The date-time format to use when transforming this property
22+
* @param bool|null $extractTypesFromGetter If true, the types will be extracted from the getter method
2223
*/
2324
public function __construct(
2425
public string|array|null $target = null,
@@ -30,6 +31,7 @@ public function __construct(
3031
public ?array $groups = null,
3132
public int $priority = 0,
3233
public ?string $dateTimeFormat = null,
34+
public ?bool $extractTypesFromGetter = null,
3335
) {
3436
}
3537
}

src/Configuration.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ public function __construct(
5050
* Allow extra properties to be mapped when the target or the source implements ArrayAccess class.
5151
*/
5252
public bool $allowExtraProperties = false,
53+
/**
54+
* When extracting types from a target property we generally try to use the one from where we write.
55+
*
56+
* However, this may cause bad type in case of covariance where the type should be extracted from the setter.
57+
*
58+
* Enable this option to extract the type from the getter instead of the setter.
59+
*/
60+
public bool $extractTypesFromGetter = false,
5361
) {
5462
}
5563
}

src/Event/PropertyMetadataEvent.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public function __construct(
3131
public ?bool $disableGroupsCheck = null,
3232
public int $priority = 0,
3333
public readonly bool $isFromDefaultExtractor = false,
34+
public ?bool $extractTypesFromGetter = null,
3435
) {
3536
}
3637
}

src/EventListener/MapFromListener.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ private function addPropertyFromTarget(GenerateMapperEvent $event, MapFrom $mapF
7171
$sourceProperty = new SourcePropertyMetadata($mapFrom->property ?? $property);
7272
$targetProperty = new TargetPropertyMetadata($property);
7373

74-
$property = new PropertyMetadataEvent(
74+
$propertyMetadata = new PropertyMetadataEvent(
7575
mapperMetadata: $event->mapperMetadata,
7676
source: $sourceProperty,
7777
target: $targetProperty,
@@ -83,12 +83,13 @@ private function addPropertyFromTarget(GenerateMapperEvent $event, MapFrom $mapF
8383
if: $mapFrom->if,
8484
groups: $mapFrom->groups,
8585
priority: $mapFrom->priority,
86+
extractTypesFromGetter: $mapFrom->extractTypesFromGetter,
8687
);
8788

88-
if (\array_key_exists($property->target->property, $event->properties) && $event->properties[$property->target->property]->priority >= $property->priority) {
89+
if (\array_key_exists($propertyMetadata->target->property, $event->properties) && $event->properties[$propertyMetadata->target->property]->priority >= $propertyMetadata->priority) {
8990
return;
9091
}
9192

92-
$event->properties[$property->target->property] = $property;
93+
$event->properties[$propertyMetadata->target->property] = $propertyMetadata;
9394
}
9495
}

src/EventListener/MapToListener.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ private function addPropertyFromSource(GenerateMapperEvent $event, MapTo $mapTo,
7272
$sourceProperty = new SourcePropertyMetadata($property);
7373
$targetProperty = new TargetPropertyMetadata($mapTo->property ?? $property);
7474

75-
$property = new PropertyMetadataEvent(
75+
$propertyMetadata = new PropertyMetadataEvent(
7676
mapperMetadata: $event->mapperMetadata,
7777
source: $sourceProperty,
7878
target: $targetProperty,
@@ -84,12 +84,13 @@ private function addPropertyFromSource(GenerateMapperEvent $event, MapTo $mapTo,
8484
if: $mapTo->if,
8585
groups: $mapTo->groups,
8686
priority: $mapTo->priority,
87+
extractTypesFromGetter: $mapTo->extractTypesFromGetter,
8788
);
8889

89-
if (\array_key_exists($property->target->property, $event->properties) && $event->properties[$property->target->property]->priority >= $property->priority) {
90+
if (\array_key_exists($propertyMetadata->target->property, $event->properties) && $event->properties[$propertyMetadata->target->property]->priority >= $propertyMetadata->priority) {
9091
return;
9192
}
9293

93-
$event->properties[$property->target->property] = $property;
94+
$event->properties[$propertyMetadata->target->property] = $propertyMetadata;
9495
}
9596
}

src/Extractor/FromSourceMappingExtractor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
*/
2121
final class FromSourceMappingExtractor extends MappingExtractor
2222
{
23-
public function getTypes(string $source, SourcePropertyMetadata $sourceProperty, string $target, TargetPropertyMetadata $targetProperty): TypesMatching
23+
public function getTypes(string $source, SourcePropertyMetadata $sourceProperty, string $target, TargetPropertyMetadata $targetProperty, bool $extractTypesFromGetter): TypesMatching
2424
{
2525
$types = new TypesMatching();
2626
$sourceTypes = $this->propertyInfoExtractor->getTypes($source, $sourceProperty->property, [

src/Extractor/FromTargetMappingExtractor.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@
2020
*/
2121
final class FromTargetMappingExtractor extends MappingExtractor
2222
{
23-
public function getTypes(string $source, SourcePropertyMetadata $sourceProperty, string $target, TargetPropertyMetadata $targetProperty): TypesMatching
23+
public function getTypes(string $source, SourcePropertyMetadata $sourceProperty, string $target, TargetPropertyMetadata $targetProperty, bool $extractTypesFromGetter): TypesMatching
2424
{
2525
$types = new TypesMatching();
2626
$targetTypes = $this->propertyInfoExtractor->getTypes($target, $targetProperty->property, [
2727
ReadWriteTypeExtractor::WRITE_MUTATOR => $targetProperty->writeMutator,
28+
ReadWriteTypeExtractor::EXTRACT_TYPE_FROM_GETTER => $extractTypesFromGetter,
2829
]) ?? [];
2930

3031
foreach ($targetTypes as $type) {

src/Extractor/MappingExtractorInterface.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ interface MappingExtractorInterface
2727
*/
2828
public function getProperties(string $class): iterable;
2929

30-
public function getTypes(string $source, SourcePropertyMetadata $sourceProperty, string $target, TargetPropertyMetadata $targetProperty): TypesMatching;
30+
public function getTypes(string $source, SourcePropertyMetadata $sourceProperty, string $target, TargetPropertyMetadata $targetProperty, bool $extractTypesFromGetter): TypesMatching;
3131

3232
public function getDateTimeFormat(PropertyMetadataEvent $propertyMetadataEvent): string;
3333

0 commit comments

Comments
 (0)