Skip to content

Commit 55549e5

Browse files
committed
fix(array): fix array and collection when using deep target populate
1 parent 6cd863c commit 55549e5

File tree

9 files changed

+137
-7
lines changed

9 files changed

+137
-7
lines changed

castor.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,14 @@ function qa_phpstan(bool $generateBaseline = false)
3535
}
3636

3737
#[AsTask('mapper', namespace: 'debug', description: 'Debug a mapper', aliases: ['debug'])]
38-
function debug_mapper(string $source, string $target)
38+
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+
}
45+
4246
$automapper = AutoMapper\AutoMapper::create();
4347
// get private property loader value
4448
$loader = new ReflectionProperty($automapper, 'classLoader');

src/Transformer/AbstractArrayTransformer.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66

77
use AutoMapper\Extractor\WriteMutator;
88
use AutoMapper\Generator\UniqueVariableScope;
9+
use AutoMapper\MapperContext;
910
use AutoMapper\Metadata\PropertyMetadata;
1011
use PhpParser\Node\Expr;
1112
use PhpParser\Node\Name;
13+
use PhpParser\Node\Scalar;
1214
use PhpParser\Node\Stmt;
1315

1416
/**
@@ -31,8 +33,21 @@ public function transform(Expr $input, Expr $target, PropertyMetadata $propertyM
3133
* $values = [];.
3234
*/
3335
$valuesVar = new Expr\Variable($uniqueVariableScope->getUniqueName('values'));
36+
$baseAssign = new Expr\Array_();
37+
38+
if ($propertyMapping->target->readAccessor !== null) {
39+
$baseAssign = new Expr\Ternary(
40+
new Expr\BinaryOp\Coalesce(
41+
new Expr\ArrayDimFetch(new Expr\Variable('context'), new Scalar\String_(MapperContext::DEEP_TARGET_TO_POPULATE)),
42+
new Expr\ConstFetch(new Name('false'))
43+
),
44+
$propertyMapping->target->readAccessor->getExpression(new Expr\Variable('result')),
45+
new Expr\Array_()
46+
);
47+
}
48+
3449
$statements = [
35-
new Stmt\Expression(new Expr\Assign($valuesVar, new Expr\Array_())),
50+
new Stmt\Expression(new Expr\Assign($valuesVar, $baseAssign)),
3651
];
3752

3853
$loopValueVar = new Expr\Variable($uniqueVariableScope->getUniqueName('value'));

src/Transformer/ArrayToDoctrineCollectionTransformer.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
namespace AutoMapper\Transformer;
66

77
use AutoMapper\Generator\UniqueVariableScope;
8+
use AutoMapper\MapperContext;
89
use AutoMapper\Metadata\PropertyMetadata;
910
use Doctrine\Common\Collections\ArrayCollection;
1011
use PhpParser\Node\Arg;
1112
use PhpParser\Node\Expr;
1213
use PhpParser\Node\Name;
14+
use PhpParser\Node\Scalar;
1315
use PhpParser\Node\Stmt;
1416

1517
/**
@@ -30,8 +32,22 @@ public function transform(Expr $input, Expr $target, PropertyMetadata $propertyM
3032
* $collection = new ArrayCollection();.
3133
*/
3234
$collectionVar = new Expr\Variable($uniqueVariableScope->getUniqueName('collection'));
35+
36+
$baseAssign = new Expr\New_(new Name(ArrayCollection::class));
37+
38+
if ($propertyMapping->target->readAccessor !== null) {
39+
$baseAssign = new Expr\Ternary(
40+
new Expr\BinaryOp\Coalesce(
41+
new Expr\ArrayDimFetch(new Expr\Variable('context'), new Scalar\String_(MapperContext::DEEP_TARGET_TO_POPULATE)),
42+
new Expr\ConstFetch(new Name('false'))
43+
),
44+
$propertyMapping->target->readAccessor->getExpression(new Expr\Variable('result')),
45+
$baseAssign,
46+
);
47+
}
48+
3349
$statements = [
34-
new Stmt\Expression(new Expr\Assign($collectionVar, new Expr\New_(new Name(ArrayCollection::class)))),
50+
new Stmt\Expression(new Expr\Assign($collectionVar, $baseAssign)),
3551
];
3652

3753
$loopValueVar = new Expr\Variable($uniqueVariableScope->getUniqueName('value'));

src/Transformer/ArrayTransformerFactory.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ protected function createTransformer(Type $sourceType, Type $targetType, SourceP
5252
$subItemTransformer = $this->chainTransformerFactory->getTransformer($types, $source, $target, $mapperMetadata);
5353

5454
if (null !== $subItemTransformer) {
55+
if ($subItemTransformer instanceof ObjectTransformer) {
56+
$subItemTransformer->deepTargetToPopulate = false;
57+
}
58+
5559
$sourceCollectionKeyTypes = $sourceType->getCollectionKeyTypes();
5660
$sourceCollectionKeyType = $sourceCollectionKeyTypes[0] ?? null;
5761

src/Transformer/DoctrineCollectionTransformerFactory.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ protected function createTransformer(Type $sourceType, Type $targetType, SourceP
3636
return null;
3737
}
3838

39+
if ($subItemTransformer instanceof ObjectTransformer) {
40+
$subItemTransformer->deepTargetToPopulate = false;
41+
}
42+
3943
return new ArrayToDoctrineCollectionTransformer($subItemTransformer);
4044
}
4145

src/Transformer/ObjectTransformer.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@
2020
*
2121
* @internal
2222
*/
23-
final readonly class ObjectTransformer implements TransformerInterface, DependentTransformerInterface, AssignedByReferenceTransformerInterface, CheckTypeInterface
23+
final class ObjectTransformer implements TransformerInterface, DependentTransformerInterface, AssignedByReferenceTransformerInterface, CheckTypeInterface
2424
{
2525
public function __construct(
26-
private Type $sourceType,
27-
private Type $targetType,
26+
private readonly Type $sourceType,
27+
private readonly Type $targetType,
28+
public bool $deepTargetToPopulate = true,
2829
) {
2930
}
3031

@@ -38,7 +39,7 @@ public function transform(Expr $input, Expr $target, PropertyMetadata $propertyM
3839
];
3940

4041
// ($context['deep_target_to_populate'] ?? false) ? $source->property : null
41-
if ($propertyMapping->target->readAccessor !== null) {
42+
if ($propertyMapping->target->readAccessor !== null && $this->deepTargetToPopulate) {
4243
$newContextArgs[] = new Arg(
4344
new Expr\Ternary(
4445
new Expr\BinaryOp\Coalesce(
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
AutoMapper\Tests\AutoMapperTest\DeepPopulateWithArrayCollection\Foo {
2+
+bars: [
3+
AutoMapper\Tests\AutoMapperTest\DeepPopulateWithArrayCollection\Bar {
4+
+bar: "bar1"
5+
}
6+
AutoMapper\Tests\AutoMapperTest\DeepPopulateWithArrayCollection\Bar {
7+
+bar: "bar2"
8+
}
9+
AutoMapper\Tests\AutoMapperTest\DeepPopulateWithArrayCollection\Bar {
10+
+bar: "bar3"
11+
}
12+
AutoMapper\Tests\AutoMapperTest\DeepPopulateWithArrayCollection\Bar {
13+
+bar: "bar4"
14+
}
15+
]
16+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
AutoMapper\Tests\AutoMapperTest\DeepPopulateWithArrayCollection\FooWithArrayCollection {
2+
+bars: Doctrine\Common\Collections\ArrayCollection {
3+
-elements: [
4+
AutoMapper\Tests\AutoMapperTest\DeepPopulateWithArrayCollection\Bar {
5+
+bar: "bar1"
6+
}
7+
AutoMapper\Tests\AutoMapperTest\DeepPopulateWithArrayCollection\Bar {
8+
+bar: "bar2"
9+
}
10+
AutoMapper\Tests\AutoMapperTest\DeepPopulateWithArrayCollection\Bar {
11+
+bar: "bar3"
12+
}
13+
AutoMapper\Tests\AutoMapperTest\DeepPopulateWithArrayCollection\Bar {
14+
+bar: "bar4"
15+
}
16+
]
17+
}
18+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace AutoMapper\Tests\AutoMapperTest\DeepPopulateWithArrayCollection;
6+
7+
use AutoMapper\Tests\AutoMapperBuilder;
8+
use Doctrine\Common\Collections\ArrayCollection;
9+
10+
class Bar
11+
{
12+
public string $bar;
13+
}
14+
15+
class Foo
16+
{
17+
/** @var array<Bar> */
18+
public array $bars;
19+
}
20+
21+
class FooWithArrayCollection
22+
{
23+
/** @var ArrayCollection<Bar> */
24+
public ArrayCollection $bars;
25+
}
26+
27+
return (function () {
28+
$autoMapper = AutoMapperBuilder::buildAutoMapper();
29+
30+
$data = [
31+
'bars' => [
32+
['bar' => 'bar3'],
33+
['bar' => 'bar4'],
34+
],
35+
];
36+
37+
$bar1 = new Bar();
38+
$bar1->bar = 'bar1';
39+
40+
$bar2 = new Bar();
41+
$bar2->bar = 'bar2';
42+
43+
$existingObject = new Foo();
44+
$existingObject->bars = [$bar1, $bar2];
45+
46+
$existingObjectWithArrayCollection = new FooWithArrayCollection();
47+
$existingObjectWithArrayCollection->bars = new ArrayCollection([$bar1, $bar2]);
48+
49+
yield 'array' => $autoMapper->map($data, $existingObject, ['deep_target_to_populate' => true]);
50+
51+
yield 'collection' => $autoMapper->map($data, $existingObjectWithArrayCollection, ['deep_target_to_populate' => true]);
52+
})();

0 commit comments

Comments
 (0)