diff --git a/CHANGELOG.md b/CHANGELOG.md index e5208f65..21e382d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ * refactor(`PropertyMapper`): If `property` is missing, use the method name, stripping the leading 'map' and lowercasing the first letter. Example: `mapName` will map to the property `name`. +* feat(`PropertyMapper`): Option to add `Context` & `MainTransformerInterface` + as extra arguments to the property mapper. ## 0.5.26 diff --git a/README.md b/README.md index be8cc4f7..9bca0f10 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,6 @@ Full documentation is available at [rekalogika.dev/mapper](https://rekalogika.de ## Future Features * Option to read & write to private properties. -* Option to inject `Context` and `MainTransformer` to a property mapper. * Data collector and profiler integration. ## Installation diff --git a/config/tests.php b/config/tests.php index 2ece8208..700da43f 100644 --- a/config/tests.php +++ b/config/tests.php @@ -16,12 +16,18 @@ use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\PropertyMapperWithClassAttributeWithoutExplicitProperty; use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\PropertyMapperWithConstructorWithClassAttribute; use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\PropertyMapperWithConstructorWithoutClassAttribute; +use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\PropertyMapperWithExtraArguments; use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\PropertyMapperWithoutClassAttribute; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; return static function (ContainerConfigurator $containerConfigurator): void { $services = $containerConfigurator->services(); + $services->defaults() + ->autowire() + ->autoconfigure() + ->public(); + // add test aliases $serviceIds = TestKernel::getServiceIds(); @@ -29,28 +35,10 @@ $services->alias('test.' . $serviceId, $serviceId)->public(); }; - $services->set(PropertyMapperWithoutClassAttribute::class) - ->autowire() - ->autoconfigure() - ->public(); - - $services->set(PropertyMapperWithClassAttribute::class) - ->autowire() - ->autoconfigure() - ->public(); - - $services->set(PropertyMapperWithConstructorWithoutClassAttribute::class) - ->autowire() - ->autoconfigure() - ->public(); - - $services->set(PropertyMapperWithConstructorWithClassAttribute::class) - ->autowire() - ->autoconfigure() - ->public(); - - $services->set(PropertyMapperWithClassAttributeWithoutExplicitProperty::class) - ->autowire() - ->autoconfigure() - ->public(); + $services->set(PropertyMapperWithoutClassAttribute::class); + $services->set(PropertyMapperWithClassAttribute::class); + $services->set(PropertyMapperWithConstructorWithoutClassAttribute::class); + $services->set(PropertyMapperWithConstructorWithClassAttribute::class); + $services->set(PropertyMapperWithClassAttributeWithoutExplicitProperty::class); + $services->set(PropertyMapperWithExtraArguments::class); }; diff --git a/src/DependencyInjection/PropertyMapperPass.php b/src/DependencyInjection/PropertyMapperPass.php index ec72a370..8f665a30 100644 --- a/src/DependencyInjection/PropertyMapperPass.php +++ b/src/DependencyInjection/PropertyMapperPass.php @@ -13,6 +13,10 @@ namespace Rekalogika\Mapper\DependencyInjection; +use Rekalogika\Mapper\Context\Context; +use Rekalogika\Mapper\Exception\InvalidArgumentException; +use Rekalogika\Mapper\MainTransformer\MainTransformerInterface; +use Rekalogika\Mapper\PropertyMapper\Contracts\PropertyMapperServicePointer; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -23,9 +27,16 @@ public function process(ContainerBuilder $container) $propertyMapperResolver = $container ->getDefinition('rekalogika.mapper.property_mapper.resolver'); - foreach ($container->findTaggedServiceIds('rekalogika.mapper.property_mapper') as $serviceId => $tags) { + $taggedServices = $container->findTaggedServiceIds('rekalogika.mapper.property_mapper'); + + foreach ($taggedServices as $serviceId => $tags) { + $serviceDefinition = $container->getDefinition($serviceId); + $serviceClass = $serviceDefinition->getClass() ?? throw new InvalidArgumentException('Class is required'); + /** @var array $tag */ foreach ($tags as $tag) { + $method = $tag['method'] ?? throw new InvalidArgumentException('Method is required'); + $propertyMapperResolver->addMethodCall( 'addPropertyMapper', [ @@ -33,10 +44,56 @@ public function process(ContainerBuilder $container) $tag['targetClass'], $tag['property'], $serviceId, - $tag['method'], + $method, + self::getExtraArguments($serviceClass, $method), ] ); } } } + + /** + * @param class-string $serviceClass + * @return array + */ + private static function getExtraArguments( + string $serviceClass, + string $method + ): array { + $reflectionClass = new \ReflectionClass($serviceClass); + $parameters = $reflectionClass->getMethod($method)->getParameters(); + // remove first element, which is always the source class + array_shift($parameters); + + $extraArguments = []; + + foreach ($parameters as $parameter) { + $type = $parameter->getType(); + + if (!$type instanceof \ReflectionNamedType) { + throw new InvalidArgumentException( + sprintf( + 'Extra arguments for property mapper "%s" in class "%s" must be type hinted.', + $method, + $serviceClass, + ) + ); + } + + $extraArguments[] = match ($type->getName()) { + Context::class => PropertyMapperServicePointer::ARGUMENT_CONTEXT, + MainTransformerInterface::class => PropertyMapperServicePointer::ARGUMENT_MAIN_TRANSFORMER, + default => throw new InvalidArgumentException( + sprintf( + 'Extra argument with type "%s" for property mapper "%s" in class "%s" is unsupported.', + $type->getName(), + $method, + $serviceClass, + ) + ) + }; + } + + return $extraArguments; + } } diff --git a/src/MapperFactory/MapperFactory.php b/src/MapperFactory/MapperFactory.php index 736a0a75..94f4d8fe 100644 --- a/src/MapperFactory/MapperFactory.php +++ b/src/MapperFactory/MapperFactory.php @@ -27,6 +27,7 @@ use Rekalogika\Mapper\ObjectCache\ObjectCacheFactory; use Rekalogika\Mapper\ObjectCache\ObjectCacheFactoryInterface; use Rekalogika\Mapper\PropertyMapper\Contracts\PropertyMapperResolverInterface; +use Rekalogika\Mapper\PropertyMapper\Contracts\PropertyMapperServicePointer; use Rekalogika\Mapper\PropertyMapper\PropertyMapperResolver; use Rekalogika\Mapper\Transformer\ArrayLikeMetadata\ArrayLikeMetadataFactory; use Rekalogika\Mapper\Transformer\ArrayLikeMetadata\Contracts\ArrayLikeMetadataFactoryInterface; @@ -81,7 +82,7 @@ class MapperFactory { /** - * @var array + * @var array}> */ private array $propertyMappers = []; @@ -139,13 +140,15 @@ public function __construct( /** * @param class-string $sourceClass * @param class-string $targetClass + * @param array $extraArguments */ public function addPropertyMapper( string $sourceClass, string $targetClass, string $property, object $service, - string $method + string $method, + array $extraArguments = [] ): void { $this->propertyMappers[] = [ 'sourceClass' => $sourceClass, @@ -153,6 +156,7 @@ public function addPropertyMapper( 'property' => $property, 'service' => $service, 'method' => $method, + 'extraArguments' => $extraArguments, ]; } @@ -559,6 +563,7 @@ protected function getPropertyMapperResolver(): PropertyMapperResolverInterface $propertyMapper['property'], $propertyMapper['service']::class, $propertyMapper['method'], + $propertyMapper['extraArguments'], ); } } diff --git a/src/PropertyMapper/Contracts/PropertyMapperResolverInterface.php b/src/PropertyMapper/Contracts/PropertyMapperResolverInterface.php index deb3b8a6..90527093 100644 --- a/src/PropertyMapper/Contracts/PropertyMapperResolverInterface.php +++ b/src/PropertyMapper/Contracts/PropertyMapperResolverInterface.php @@ -15,18 +15,6 @@ interface PropertyMapperResolverInterface { - /** - * @param class-string $sourceClass - * @param class-string $targetClass - */ - public function addPropertyMapper( - string $sourceClass, - string $targetClass, - string $property, - string $serviceId, - string $method - ): void; - /** * @param class-string $sourceClass * @param class-string $targetClass diff --git a/src/PropertyMapper/Contracts/PropertyMapperServicePointer.php b/src/PropertyMapper/Contracts/PropertyMapperServicePointer.php index ee4d379a..9a002f3e 100644 --- a/src/PropertyMapper/Contracts/PropertyMapperServicePointer.php +++ b/src/PropertyMapper/Contracts/PropertyMapperServicePointer.php @@ -15,9 +15,16 @@ final readonly class PropertyMapperServicePointer { + public const ARGUMENT_CONTEXT = 'context'; + public const ARGUMENT_MAIN_TRANSFORMER = 'main_transformer'; + + /** + * @param array $extraArguments + */ public function __construct( private string $serviceId, private string $method, + private array $extraArguments, ) { } @@ -30,4 +37,12 @@ public function getMethod(): string { return $this->method; } + + /** + * @return array + */ + public function getExtraArguments(): array + { + return $this->extraArguments; + } } diff --git a/src/PropertyMapper/PropertyMapperResolver.php b/src/PropertyMapper/PropertyMapperResolver.php index 730ad9dc..35a70896 100644 --- a/src/PropertyMapper/PropertyMapperResolver.php +++ b/src/PropertyMapper/PropertyMapperResolver.php @@ -26,16 +26,18 @@ class PropertyMapperResolver implements PropertyMapperResolverInterface /** * @param class-string $sourceClass * @param class-string $targetClass + * @param array $extraArguments */ public function addPropertyMapper( string $sourceClass, string $targetClass, string $property, string $serviceId, - string $method + string $method, + array $extraArguments = [] ): void { $this->propertyMappers[$targetClass][$property][$sourceClass] - = new PropertyMapperServicePointer($serviceId, $method); + = new PropertyMapperServicePointer($serviceId, $method, $extraArguments); } /** diff --git a/src/Transformer/ObjectToObjectTransformer.php b/src/Transformer/ObjectToObjectTransformer.php index 42c9104e..90e5cde7 100644 --- a/src/Transformer/ObjectToObjectTransformer.php +++ b/src/Transformer/ObjectToObjectTransformer.php @@ -17,6 +17,7 @@ use Rekalogika\Mapper\Context\Context; use Rekalogika\Mapper\Exception\InvalidArgumentException; use Rekalogika\Mapper\ObjectCache\ObjectCache; +use Rekalogika\Mapper\PropertyMapper\Contracts\PropertyMapperServicePointer; use Rekalogika\Mapper\Transformer\Contracts\MainTransformerAwareInterface; use Rekalogika\Mapper\Transformer\Contracts\MainTransformerAwareTrait; use Rekalogika\Mapper\Transformer\Contracts\TransformerInterface; @@ -255,12 +256,22 @@ private function transformValue( $propertyMapper = $this->propertyMapperLocator ->get($propertyMapperPointer->getServiceId()); + $extraArguments = $propertyMapperPointer->getExtraArguments(); + $extraArgumentsParams = []; + + foreach ($extraArguments as $extraArgument) { + $extraArgumentsParams[] = match ($extraArgument) { + PropertyMapperServicePointer::ARGUMENT_CONTEXT => $context, + PropertyMapperServicePointer::ARGUMENT_MAIN_TRANSFORMER => $this->getMainTransformer(), + }; + } + /** * @psalm-suppress MixedAssignment * @psalm-suppress MixedMethodCall */ $targetPropertyValue = $propertyMapper->{$propertyMapperPointer - ->getMethod()}($source); + ->getMethod()}($source, ...$extraArgumentsParams); /** @psalm-suppress MixedAssignment */ return $targetPropertyValue; diff --git a/tests/Common/AbstractFrameworkTest.php b/tests/Common/AbstractFrameworkTest.php new file mode 100644 index 00000000..dcc0d6ad --- /dev/null +++ b/tests/Common/AbstractFrameworkTest.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +namespace Rekalogika\Mapper\Tests\Common; + +use PHPUnit\Framework\TestCase; +use Rekalogika\Mapper\MapperInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; + +abstract class AbstractFrameworkTest extends TestCase +{ + private ContainerInterface $container; + /** @psalm-suppress MissingConstructor */ + protected MapperInterface $mapper; + + public function setUp(): void + { + $kernel = new TestKernel(); + $kernel->boot(); + $this->container = $kernel->getContainer(); + $this->mapper = $this->get(MapperInterface::class); + } + + /** + * @template T of object + * @param string|class-string $serviceId + * @return ($serviceId is class-string ? T : object) + */ + public function get(string $serviceId): object + { + try { + $result = $this->container->get('test.' . $serviceId); + } catch (ServiceNotFoundException) { + /** @psalm-suppress PossiblyNullReference */ + $result = $this->container->get($serviceId); + } + + + if (class_exists($serviceId) || interface_exists($serviceId)) { + $this->assertInstanceOf($serviceId, $result); + } + + /** @psalm-suppress RedundantConditionGivenDocblockType */ + $this->assertNotNull($result); + + return $result; + } +} diff --git a/tests/Common/TestKernel.php b/tests/Common/TestKernel.php index 636ed5b5..9f396ec2 100644 --- a/tests/Common/TestKernel.php +++ b/tests/Common/TestKernel.php @@ -13,6 +13,7 @@ namespace Rekalogika\Mapper\Tests\Common; +use Rekalogika\Mapper\MapperInterface; use Rekalogika\Mapper\Mapping\MappingFactoryInterface; use Rekalogika\Mapper\RekalogikaMapperBundle; use Rekalogika\Mapper\Transformer\ArrayToObjectTransformer; @@ -69,6 +70,8 @@ public function registerContainerConfiguration(LoaderInterface $loader): void */ public static function getServiceIds(): iterable { + yield MapperInterface::class; + yield 'rekalogika.mapper.property_info'; yield 'rekalogika.mapper.cache.property_info'; yield 'rekalogika.mapper.property_info.cache'; diff --git a/tests/Fixtures/PropertyMapper/PropertyMapperWithExtraArguments.php b/tests/Fixtures/PropertyMapper/PropertyMapperWithExtraArguments.php new file mode 100644 index 00000000..a58d53e1 --- /dev/null +++ b/tests/Fixtures/PropertyMapper/PropertyMapperWithExtraArguments.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +namespace Rekalogika\Mapper\Tests\Fixtures\PropertyMapper; + +use Rekalogika\Mapper\Context\Context; +use Rekalogika\Mapper\MainTransformer\MainTransformerInterface; +use Rekalogika\Mapper\PropertyMapper\AsPropertyMapper; + +#[AsPropertyMapper(targetClass: SomeObjectDto::class)] +class PropertyMapperWithExtraArguments +{ + #[AsPropertyMapper] + public function mapPropertyE( + SomeObject $object, + Context $context, + MainTransformerInterface $mainTransformer + ): string { + return sprintf( + 'I have "%s" and "%s" that I can use to transform source property "%s"', + $context::class, + $mainTransformer::class, + $object::class, + ); + } +} diff --git a/tests/Fixtures/PropertyMapper/SomeObjectDto.php b/tests/Fixtures/PropertyMapper/SomeObjectDto.php index 4c34117c..7ed29799 100644 --- a/tests/Fixtures/PropertyMapper/SomeObjectDto.php +++ b/tests/Fixtures/PropertyMapper/SomeObjectDto.php @@ -19,4 +19,5 @@ class SomeObjectDto public ?string $propertyB = null; public ?string $propertyC = null; public ?string $propertyD = null; + public ?string $propertyE = null; } diff --git a/tests/FrameworkTest/FrameworkTest.php b/tests/FrameworkTest/FrameworkTest.php index 5028df77..1a3ce8c9 100644 --- a/tests/FrameworkTest/FrameworkTest.php +++ b/tests/FrameworkTest/FrameworkTest.php @@ -15,17 +15,7 @@ use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; -use Rekalogika\Mapper\PropertyMapper\Contracts\PropertyMapperResolverInterface; -use Rekalogika\Mapper\PropertyMapper\Contracts\PropertyMapperServicePointer; use Rekalogika\Mapper\Tests\Common\TestKernel; -use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\PropertyMapperWithClassAttribute; -use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\PropertyMapperWithClassAttributeWithoutExplicitProperty; -use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\PropertyMapperWithConstructorWithClassAttribute; -use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\PropertyMapperWithConstructorWithoutClassAttribute; -use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\PropertyMapperWithoutClassAttribute; -use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\SomeObject; -use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\SomeObjectDto; -use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\SomeObjectWithConstructorDto; class FrameworkTest extends TestCase { @@ -40,102 +30,10 @@ public function setUp(): void public function testWiring(): void { - foreach (TestKernel::getServiceIds() as $serviceId) { $service = $this->container?->get('test.' . $serviceId); $this->assertIsObject($service); } } - - public function testPropertyMapperRegistration(): void - { - $propertyMapperA = $this->container?->get(PropertyMapperWithoutClassAttribute::class); - $propertyMapperB = $this->container?->get(PropertyMapperWithClassAttribute::class); - - $this->assertInstanceOf(PropertyMapperWithoutClassAttribute::class, $propertyMapperA); - $this->assertInstanceOf(PropertyMapperWithClassAttribute::class, $propertyMapperB); - - $propertyMapperResolver = $this->container?->get('test.rekalogika.mapper.property_mapper.resolver'); - - $this->assertInstanceOf(PropertyMapperResolverInterface::class, $propertyMapperResolver); - - $result1 = $propertyMapperResolver->getPropertyMapper( - SomeObject::class, - SomeObjectDto::class, - 'propertyA' - ); - - $this->assertEquals( - new PropertyMapperServicePointer( - PropertyMapperWithoutClassAttribute::class, - 'mapPropertyA' - ), - $result1, - ); - - $result2 = $propertyMapperResolver->getPropertyMapper( - SomeObject::class, - SomeObjectDto::class, - 'propertyB' - ); - - $this->assertEquals( - new PropertyMapperServicePointer( - PropertyMapperWithClassAttribute::class, - 'mapPropertyB' - ), - $result2, - ); - - $result3 = $propertyMapperResolver->getPropertyMapper( - SomeObject::class, - SomeObjectDto::class, - 'propertyC' - ); - - $this->assertNull($result3); - - $result4 = $propertyMapperResolver->getPropertyMapper( - SomeObject::class, - SomeObjectWithConstructorDto::class, - 'propertyA' - ); - - $this->assertEquals( - new PropertyMapperServicePointer( - PropertyMapperWithConstructorWithoutClassAttribute::class, - 'mapPropertyA' - ), - $result4, - ); - - $result5 = $propertyMapperResolver->getPropertyMapper( - SomeObject::class, - SomeObjectWithConstructorDto::class, - 'propertyB' - ); - - $this->assertEquals( - new PropertyMapperServicePointer( - PropertyMapperWithConstructorWithClassAttribute::class, - 'mapPropertyB' - ), - $result5, - ); - - $result6 = $propertyMapperResolver->getPropertyMapper( - SomeObject::class, - SomeObjectDto::class, - 'propertyD' - ); - - $this->assertEquals( - new PropertyMapperServicePointer( - PropertyMapperWithClassAttributeWithoutExplicitProperty::class, - 'mapPropertyD' - ), - $result6, - ); - } } diff --git a/tests/IntegrationTest/PropertyMappingTest.php b/tests/IntegrationTest/PropertyMappingTest.php index 341b325f..0b4fdab6 100644 --- a/tests/IntegrationTest/PropertyMappingTest.php +++ b/tests/IntegrationTest/PropertyMappingTest.php @@ -13,49 +13,135 @@ namespace Rekalogika\Mapper\Tests\IntegrationTest; -use Rekalogika\Mapper\Tests\Common\AbstractIntegrationTest; +use Rekalogika\Mapper\Context\Context; +use Rekalogika\Mapper\MainTransformer\MainTransformer; +use Rekalogika\Mapper\PropertyMapper\Contracts\PropertyMapperResolverInterface; +use Rekalogika\Mapper\PropertyMapper\Contracts\PropertyMapperServicePointer; +use Rekalogika\Mapper\Tests\Common\AbstractFrameworkTest; use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\PropertyMapperWithClassAttribute; +use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\PropertyMapperWithClassAttributeWithoutExplicitProperty; use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\PropertyMapperWithConstructorWithClassAttribute; use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\PropertyMapperWithConstructorWithoutClassAttribute; +use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\PropertyMapperWithExtraArguments; use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\PropertyMapperWithoutClassAttribute; use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\SomeObject; use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\SomeObjectDto; use Rekalogika\Mapper\Tests\Fixtures\PropertyMapper\SomeObjectWithConstructorDto; -class PropertyMappingTest extends AbstractIntegrationTest +class PropertyMappingTest extends AbstractFrameworkTest { - protected function getPropertyMappers(): iterable + public function testPropertyMapperRegistration(): void { + $propertyMapperA = $this->get(PropertyMapperWithoutClassAttribute::class); + $propertyMapperB = $this->get(PropertyMapperWithClassAttribute::class); + + $this->assertInstanceOf(PropertyMapperWithoutClassAttribute::class, $propertyMapperA); + $this->assertInstanceOf(PropertyMapperWithClassAttribute::class, $propertyMapperB); + + $propertyMapperResolver = $this->get('rekalogika.mapper.property_mapper.resolver'); + + $this->assertInstanceOf(PropertyMapperResolverInterface::class, $propertyMapperResolver); + } + + /** + * @dataProvider propertyMapperResolverDataProvider + * @param class-string $sourceClass + * @param class-string $targetClass + */ + public function testPropertyMapperResolver( + string $sourceClass, + string $targetClass, + string $property, + ?PropertyMapperServicePointer $expected + ): void { + $propertyMapperResolver = $this->get('rekalogika.mapper.property_mapper.resolver'); + + $this->assertInstanceOf(PropertyMapperResolverInterface::class, $propertyMapperResolver); + + $result = $propertyMapperResolver->getPropertyMapper($sourceClass, $targetClass, $property); + + $this->assertEquals($expected, $result); + } + + /** + * @return iterable + */ + public function propertyMapperResolverDataProvider(): iterable + { + yield [ + SomeObject::class, + SomeObjectDto::class, + 'propertyA', + new PropertyMapperServicePointer( + PropertyMapperWithoutClassAttribute::class, + 'mapPropertyA', + [] + ), + ]; + + yield [ + SomeObject::class, + SomeObjectDto::class, + 'propertyB', + new PropertyMapperServicePointer( + PropertyMapperWithClassAttribute::class, + 'mapPropertyB', + [] + ), + ]; + + yield [ + SomeObject::class, + SomeObjectDto::class, + 'propertyC', + null, + ]; + yield [ - 'sourceClass' => SomeObject::class, - 'targetClass' => SomeObjectDto::class, - 'property' => 'propertyA', - 'service' => new PropertyMapperWithoutClassAttribute(), - 'method' => 'mapPropertyA', + SomeObject::class, + SomeObjectWithConstructorDto::class, + 'propertyA', + new PropertyMapperServicePointer( + PropertyMapperWithConstructorWithoutClassAttribute::class, + 'mapPropertyA', + [] + ), ]; yield [ - 'sourceClass' => SomeObject::class, - 'targetClass' => SomeObjectDto::class, - 'property' => 'propertyB', - 'service' => new PropertyMapperWithClassAttribute(), - 'method' => 'mapPropertyB', + SomeObject::class, + SomeObjectWithConstructorDto::class, + 'propertyB', + new PropertyMapperServicePointer( + PropertyMapperWithConstructorWithClassAttribute::class, + 'mapPropertyB', + [] + ), ]; yield [ - 'sourceClass' => SomeObject::class, - 'targetClass' => SomeObjectWithConstructorDto::class, - 'property' => 'propertyA', - 'service' => new PropertyMapperWithConstructorWithoutClassAttribute(), - 'method' => 'mapPropertyA', + SomeObject::class, + SomeObjectDto::class, + 'propertyD', + new PropertyMapperServicePointer( + PropertyMapperWithClassAttributeWithoutExplicitProperty::class, + 'mapPropertyD', + [] + ), ]; yield [ - 'sourceClass' => SomeObject::class, - 'targetClass' => SomeObjectWithConstructorDto::class, - 'property' => 'propertyB', - 'service' => new PropertyMapperWithConstructorWithClassAttribute(), - 'method' => 'mapPropertyB', + SomeObject::class, + SomeObjectDto::class, + 'propertyE', + new PropertyMapperServicePointer( + PropertyMapperWithExtraArguments::class, + 'mapPropertyE', + [ + PropertyMapperServicePointer::ARGUMENT_CONTEXT, + PropertyMapperServicePointer::ARGUMENT_MAIN_TRANSFORMER, + ] + ), ]; } @@ -66,6 +152,14 @@ public function testPropertyMapping(): void $this->assertEquals(SomeObject::class . '::propertyA', $dto->propertyA); $this->assertEquals(SomeObject::class . '::propertyB', $dto->propertyB); + $this->assertNull($dto->propertyC); + $this->assertEquals(SomeObject::class . '::propertyD', $dto->propertyD); + $this->assertEquals(sprintf( + 'I have "%s" and "%s" that I can use to transform source property "%s"', + Context::class, + MainTransformer::class, + SomeObject::class, + ), $dto->propertyE); } public function testPropertyMappingWithConstructor(): void