Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 53b56d6

Browse filesBrowse files
committed
[ObjectMapper] Condition to target a specific class
1 parent 1a8b82e commit 53b56d6
Copy full SHA for 53b56d6

File tree

11 files changed

+133
-16
lines changed
Filter options

11 files changed

+133
-16
lines changed

‎src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/ObjectMapper/TransformCallable.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/ObjectMapper/TransformCallable.php
+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*/
1919
final class TransformCallable implements TransformCallableInterface
2020
{
21-
public function __invoke(mixed $value, object $object): mixed
21+
public function __invoke(mixed $value, object $source, ?object $target): mixed
2222
{
2323
return 'transformed';
2424
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\ObjectMapper\Condition;
13+
14+
use Symfony\Component\ObjectMapper\ConditionCallableInterface;
15+
16+
17+
/**
18+
* @implements ConditionCallableInterface<object, object>
19+
*/
20+
final class TargetClass implements ConditionCallableInterface
21+
{
22+
/**
23+
* @param class-string $className
24+
*/
25+
public function __construct(private readonly string $className)
26+
{
27+
}
28+
29+
public function __invoke(mixed $value, object $source, ?object $target): bool
30+
{
31+
return $target instanceof $this->className;
32+
}
33+
}

‎src/Symfony/Component/ObjectMapper/ConditionCallableInterface.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/ObjectMapper/ConditionCallableInterface.php
+3-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* Service used by "Map::if".
1616
*
1717
* @template T of object
18+
* @template T2 of object
1819
*
1920
* @experimental
2021
*
@@ -25,6 +26,7 @@ interface ConditionCallableInterface
2526
/**
2627
* @param mixed $value The value being mapped
2728
* @param T $source The object we're working on
29+
* @param T2|null $target The target we're mapping to
2830
*/
29-
public function __invoke(mixed $value, object $source): bool;
31+
public function __invoke(mixed $value, object $source, ?object $target): bool;
3032
}

‎src/Symfony/Component/ObjectMapper/ObjectMapper.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/ObjectMapper/ObjectMapper.php
+11-11
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public function map(object $source, object|string|null $target = null): object
7070

7171
$mappedTarget = $mappingToObject ? $target : $targetRefl->newInstanceWithoutConstructor();
7272
if ($map && $map->transform) {
73-
$mappedTarget = $this->applyTransforms($map, $mappedTarget, $mappedTarget);
73+
$mappedTarget = $this->applyTransforms($map, $mappedTarget, $mappedTarget, null);
7474

7575
if (!\is_object($mappedTarget)) {
7676
throw new MappingTransformException(\sprintf('Cannot map "%s" to a non-object target of type "%s".', get_debug_type($source), get_debug_type($mappedTarget)));
@@ -123,7 +123,7 @@ public function map(object $source, object|string|null $target = null): object
123123
}
124124

125125
$value = $this->getRawValue($source, $sourcePropertyName);
126-
if (($if = $mapping->if) && ($fn = $this->getCallable($if, $this->conditionCallableLocator)) && !$this->call($fn, $value, $source)) {
126+
if (($if = $mapping->if) && ($fn = $this->getCallable($if, $this->conditionCallableLocator)) && !$this->call($fn, $value, $source, $mappedTarget)) {
127127
continue;
128128
}
129129

@@ -173,16 +173,16 @@ private function getRawValue(object $source, string $propertyName): mixed
173173
private function getSourceValue(object $source, object $target, mixed $value, \SplObjectStorage $objectMap, ?Mapping $mapping = null): mixed
174174
{
175175
if ($mapping?->transform) {
176-
$value = $this->applyTransforms($mapping, $value, $source);
176+
$value = $this->applyTransforms($mapping, $value, $source, $target);
177177
}
178178

179179
if (
180180
\is_object($value)
181181
&& ($innerMetadata = $this->metadataFactory->create($value))
182-
&& ($mapTo = $this->getMapTarget($innerMetadata, $value, $source))
182+
&& ($mapTo = $this->getMapTarget($innerMetadata, $value, $source, $target))
183183
&& (\is_string($mapTo->target) && class_exists($mapTo->target))
184184
) {
185-
$value = $this->applyTransforms($mapTo, $value, $source);
185+
$value = $this->applyTransforms($mapTo, $value, $source, $target);
186186

187187
if ($value === $source) {
188188
$value = $target;
@@ -216,23 +216,23 @@ private function storeValue(string $propertyName, array &$mapToProperties, array
216216
/**
217217
* @param callable(): mixed $fn
218218
*/
219-
private function call(callable $fn, mixed $value, object $object): mixed
219+
private function call(callable $fn, mixed $value, object $source, ?object $target = null): mixed
220220
{
221221
if (\is_string($fn)) {
222222
return \call_user_func($fn, $value);
223223
}
224224

225-
return $fn($value, $object);
225+
return $fn($value, $source, $target);
226226
}
227227

228228
/**
229229
* @param Mapping[] $metadata
230230
*/
231-
private function getMapTarget(array $metadata, mixed $value, object $source): ?Mapping
231+
private function getMapTarget(array $metadata, mixed $value, object $source, ?object $target = null): ?Mapping
232232
{
233233
$mapTo = null;
234234
foreach ($metadata as $mapAttribute) {
235-
if (($if = $mapAttribute->if) && ($fn = $this->getCallable($if, $this->conditionCallableLocator)) && !$this->call($fn, $value, $source)) {
235+
if (($if = $mapAttribute->if) && ($fn = $this->getCallable($if, $this->conditionCallableLocator)) && !$this->call($fn, $value, $source, $target)) {
236236
continue;
237237
}
238238

@@ -242,7 +242,7 @@ private function getMapTarget(array $metadata, mixed $value, object $source): ?M
242242
return $mapTo;
243243
}
244244

245-
private function applyTransforms(Mapping $map, mixed $value, object $object): mixed
245+
private function applyTransforms(Mapping $map, mixed $value, object $source, ?object $target): mixed
246246
{
247247
if (!$transforms = $map->transform) {
248248
return $value;
@@ -256,7 +256,7 @@ private function applyTransforms(Mapping $map, mixed $value, object $object): mi
256256

257257
foreach ($transforms as $transform) {
258258
if ($fn = $this->getCallable($transform, $this->transformCallableLocator)) {
259-
$value = $this->call($fn, $value, $object);
259+
$value = $this->call($fn, $value, $source, $target);
260260
}
261261
}
262262

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\ObjectMapper\Tests\Fixtures\MultipleTargetProperty;
13+
14+
use Symfony\Component\ObjectMapper\Attribute\Map;
15+
use Symfony\Component\ObjectMapper\Condition\TargetClass;
16+
17+
#[Map(target: B::class)]
18+
#[Map(target: C::class)]
19+
class A
20+
{
21+
#[Map(target: 'foo', transform: 'strtoupper', if: new TargetClass(B::class))]
22+
#[Map(target: 'bar')]
23+
public string $something = 'test';
24+
25+
public string $doesNotExistInTargetB = 'foo';
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\ObjectMapper\Tests\Fixtures\MultipleTargetProperty;
13+
14+
class B
15+
{
16+
public string $foo;
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\ObjectMapper\Tests\Fixtures\MultipleTargetProperty;
13+
14+
class C
15+
{
16+
public string $foo = 'donotmap';
17+
public string $bar;
18+
public string $doesNotExistInTargetB;
19+
}

‎src/Symfony/Component/ObjectMapper/Tests/Fixtures/ServiceLocator/ConditionCallable.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/ObjectMapper/Tests/Fixtures/ServiceLocator/ConditionCallable.php
+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*/
1919
class ConditionCallable implements ConditionCallableInterface
2020
{
21-
public function __invoke(mixed $value, object $object): bool
21+
public function __invoke(mixed $value, object $source, ?object $target = null): bool
2222
{
2323
return 'ok' === $value;
2424
}

‎src/Symfony/Component/ObjectMapper/Tests/Fixtures/ServiceLocator/TransformCallable.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/ObjectMapper/Tests/Fixtures/ServiceLocator/TransformCallable.php
+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*/
1919
class TransformCallable implements TransformCallableInterface
2020
{
21-
public function __invoke(mixed $value, object $object): mixed
21+
public function __invoke(mixed $value, object $source, ?object $target = null): mixed
2222
{
2323
return "transformed$value";
2424
}

‎src/Symfony/Component/ObjectMapper/Tests/ObjectMapperTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/ObjectMapper/Tests/ObjectMapperTest.php
+18
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@
3838
use Symfony\Component\ObjectMapper\Tests\Fixtures\MapStruct\MapStructMapperMetadataFactory;
3939
use Symfony\Component\ObjectMapper\Tests\Fixtures\MapStruct\Source;
4040
use Symfony\Component\ObjectMapper\Tests\Fixtures\MapStruct\Target;
41+
use Symfony\Component\ObjectMapper\Tests\Fixtures\MultipleTargetProperty\A as MultipleTargetPropertyA;
42+
use Symfony\Component\ObjectMapper\Tests\Fixtures\MultipleTargetProperty\B as MultipleTargetPropertyB;
43+
use Symfony\Component\ObjectMapper\Tests\Fixtures\MultipleTargetProperty\C as MultipleTargetPropertyC;
4144
use Symfony\Component\ObjectMapper\Tests\Fixtures\MultipleTargets\A as MultipleTargetsA;
4245
use Symfony\Component\ObjectMapper\Tests\Fixtures\MultipleTargets\C as MultipleTargetsC;
4346
use Symfony\Component\ObjectMapper\Tests\Fixtures\Recursion\AB;
@@ -262,4 +265,19 @@ public function testTransformToWrongObject()
262265
$mapper = new ObjectMapper($metadata);
263266
$mapper->map($u);
264267
}
268+
269+
public function testMultipleTargetMapProperty()
270+
{
271+
$u = new MultipleTargetPropertyA();
272+
273+
$mapper = new ObjectMapper();
274+
$b = $mapper->map($u, MultipleTargetPropertyB::class);
275+
$this->assertInstanceOf(MultipleTargetPropertyB::class, $b);
276+
$this->assertEquals($b->foo, 'TEST');
277+
$c = $mapper->map($u, MultipleTargetPropertyC::class);
278+
$this->assertInstanceOf(MultipleTargetPropertyC::class, $c);
279+
$this->assertEquals($c->bar, 'test');
280+
$this->assertEquals($c->foo, 'donotmap');
281+
$this->assertEquals($c->doesNotExistInTargetB, 'foo');
282+
}
265283
}

‎src/Symfony/Component/ObjectMapper/TransformCallableInterface.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/ObjectMapper/TransformCallableInterface.php
+3-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* Service used by "Map::transform".
1616
*
1717
* @template T of object
18+
* @template T2 of object
1819
*
1920
* @experimental
2021
*
@@ -25,6 +26,7 @@ interface TransformCallableInterface
2526
/**
2627
* @param mixed $value The value being mapped
2728
* @param T $source The object we're working on
29+
* @param T2|null $target The target we're mapping to
2830
*/
29-
public function __invoke(mixed $value, object $source): mixed;
31+
public function __invoke(mixed $value, object $source, ?object $target): mixed;
3032
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.