Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
[ObjectMapper] Provide the target object to the map condition
And add TargetClass condition
  • Loading branch information
GromNaN committed Apr 1, 2025
commit c0a40728ad70500a01d1e7ba6e989c987ffdef15
28 changes: 28 additions & 0 deletions src/Symfony/Component/ObjectMapper/Condition/All.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Symfony\Component\ObjectMapper\Condition;

use Symfony\Component\ObjectMapper\ConditionCallableInterface;

final readonly class All implements ConditionCallableInterface
{
/**
* @param ConditionCallableInterface[] $conditions
*/
private array $conditions;
public function __construct(ConditionCallableInterface ...$conditions)
{
$this->conditions = $conditions;
}

public function __invoke(mixed $value, object $source, ?object $target = null): bool
{
foreach ($this->conditions as $condition) {
if (!$condition($value, $source, $target)) {
return false;
}
}

return true;
}
}
28 changes: 28 additions & 0 deletions src/Symfony/Component/ObjectMapper/Condition/Any.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Symfony\Component\ObjectMapper\Condition;

use Symfony\Component\ObjectMapper\ConditionCallableInterface;

final readonly class Any implements ConditionCallableInterface
{
/**
* @param ConditionCallableInterface[] $conditions
*/
private array $conditions;
public function __construct(ConditionCallableInterface ...$conditions)
{
$this->conditions = $conditions;
}

public function __invoke(mixed $value, object $source, ?object $target = null): bool
{
foreach ($this->conditions as $condition) {
if ($condition($value, $source, $target)) {
return true;
}
}

return false;
}
}
15 changes: 15 additions & 0 deletions src/Symfony/Component/ObjectMapper/Condition/TargetClass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace Symfony\Component\ObjectMapper\Condition;

use Symfony\Component\ObjectMapper\ConditionCallableInterface;

final readonly class TargetClass implements ConditionCallableInterface
{
public function __construct(private string $targetClass) {}

public function __invoke(mixed $value, object $source, ?object $target = null): bool
{
return $target && is_a($target, $this->targetClass, true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@
*
* @experimental
*
* {@see Symfony\Component\ObjectMapper\Attribute\Map}
* {@see \Symfony\Component\ObjectMapper\Attribute\Map}
*/
interface ConditionCallableInterface
{
/**
* @param mixed $value The value being mapped
* @param T $object The object we're working on
* @param mixed $value The value being mapped
* @param T $source The source object we're working on
* @param object|null $target The target object we're working on
*/
public function __invoke(mixed $value, object $object): bool;
public function __invoke(mixed $value, object $source, ?object $target = null): bool;
}
15 changes: 6 additions & 9 deletions src/Symfony/Component/ObjectMapper/ObjectMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,17 +117,13 @@ public function map(object $source, object|string|null $target = null): object
$propertyName = $property->getName();
$mappings = $this->metadataFactory->create($readMetadataFrom, $propertyName);
foreach ($mappings as $mapping) {
if (\is_string($mapping->if) && !$this->conditionCallableLocator?->has($mapping->if) && !is_a($mapping->if, $targetRefl->getName(), true)) {
continue;
}

$sourcePropertyName = $propertyName;
if ($mapping->source && (!$refl->hasProperty($propertyName) || !isset($source->$propertyName))) {
$sourcePropertyName = $mapping->source;
}

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

Expand Down Expand Up @@ -218,15 +214,16 @@ private function storeValue(string $propertyName, array &$mapToProperties, array
}

/**
* @param object $target
* @param callable(): mixed $fn
*/
private function call(callable $fn, mixed $value, object $object): mixed
private function call(callable $fn, mixed $value, object $source, ?object $target): mixed
{
if (\is_string($fn)) {
return \call_user_func($fn, $value);
}

return $fn($value, $object);
return $fn($value, $source, $target);
}

/**
Expand All @@ -236,7 +233,7 @@ private function getMapTarget(array $metadata, mixed $value, object $source): ?M
{
$mapTo = null;
foreach ($metadata as $mapAttribute) {
if (($if = $mapAttribute->if) && ($fn = $this->getCallable($if, $this->conditionCallableLocator)) && !$this->call($fn, $value, $source)) {
if (($if = $mapAttribute->if) && ($fn = $this->getCallable($if, $this->conditionCallableLocator)) && !$this->call($fn, $value, $source, null)) {
continue;
}

Expand All @@ -260,7 +257,7 @@ private function applyTransforms(Mapping $map, mixed $value, object $object): mi

foreach ($transforms as $transform) {
if ($fn = $this->getCallable($transform, $this->transformCallableLocator)) {
$value = $this->call($fn, $value, $object);
$value = $this->call($fn, $value, $object, null);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@
namespace Symfony\Component\ObjectMapper\Tests\Fixtures\MultipleTargetProperty;

use Symfony\Component\ObjectMapper\Attribute\Map;
use Symfony\Component\ObjectMapper\Condition\TargetClass;

#[Map(target: B::class)]
#[Map(target: C::class)]
class A
{
#[Map(target: 'foo', transform: 'strtoupper', if: B::class)]
#[Map(target: 'foo', transform: 'strtoupper', if: new TargetClass(B::class))]
#[Map(target: 'bar')]
public string $something = 'test';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*/
class ConditionCallable implements ConditionCallableInterface
{
public function __invoke(mixed $value, object $object): bool
public function __invoke(mixed $value, object $source, ?object $target = null): bool
{
return 'ok' === $value;
}
Expand Down