diff --git a/src/AutoMapperBundle.php b/src/AutoMapperBundle.php index 173b3f7..aa4b1af 100644 --- a/src/AutoMapperBundle.php +++ b/src/AutoMapperBundle.php @@ -4,6 +4,7 @@ use AutoMapper\Bundle\DependencyInjection\Compiler\MapperConfigurationPass; use AutoMapper\Bundle\DependencyInjection\Compiler\TransformerFactoryPass; +use Jane\Bundle\AutoMapperBundle\DependencyInjection\Compiler\PropertyInfoPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; @@ -14,6 +15,7 @@ public function build(ContainerBuilder $container) parent::build($container); $container->addCompilerPass(new MapperConfigurationPass()); + $container->addCompilerPass(new PropertyInfoPass()); $container->addCompilerPass(new TransformerFactoryPass()); } } diff --git a/src/DependencyInjection/Compiler/PropertyInfoPass.php b/src/DependencyInjection/Compiler/PropertyInfoPass.php new file mode 100644 index 0000000..f5f5046 --- /dev/null +++ b/src/DependencyInjection/Compiler/PropertyInfoPass.php @@ -0,0 +1,81 @@ +has('property_info')) { + return; + } + + $propertyInfoDefinition = $container->findDefinition('property_info'); + + $container->setDefinition( + 'automapper.property_info.reflection_extractor.inner', + new Definition( + ReflectionExtractor::class, + [ + '$accessFlags' => ReflectionExtractor::ALLOW_PUBLIC | ReflectionExtractor::ALLOW_PROTECTED | ReflectionExtractor::ALLOW_PRIVATE, + ] + ) + ); + + $container->setDefinition( + 'automapper.property_info.reflection_extractor', + new Definition( + MapToContextPropertyInfoExtractorDecorator::class, + [ + new Reference('automapper.property_info.reflection_extractor.inner'), + ] + ) + ); + + $container->setDefinition( + 'automapper.property_info', + new Definition( + PropertyInfoExtractor::class, + [ + $this->replaceReflectionExtractor($propertyInfoDefinition->getArgument(0)), + $this->replaceReflectionExtractor($propertyInfoDefinition->getArgument(1)), + $this->replaceReflectionExtractor($propertyInfoDefinition->getArgument(2)), + $this->replaceReflectionExtractor($propertyInfoDefinition->getArgument(3)), + $this->replaceReflectionExtractor($propertyInfoDefinition->getArgument(4)), + ] + ) + ); + + $container->setDefinition( + 'automapper.property_info.cache', + new Definition(PropertyInfoCacheExtractor::class, [ + new Reference('.inner'), + new Reference('cache.property_info'), + ]) + )->setDecoratedService('automapper.property_info'); + } + + private function replaceReflectionExtractor(IteratorArgument $extractors): IteratorArgument + { + $newExtractors = []; + + /** @var Reference $extractor */ + foreach ($extractors->getValues() as $extractor) { + $newExtractors[] = (string) $extractor === 'property_info.reflection_extractor' + ? new Reference('automapper.property_info.reflection_extractor') + : $extractor; + } + + return new IteratorArgument($newExtractors); + } +} diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index f6d1faa..f4cbb76 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -29,6 +29,7 @@ public function getConfigTreeBuilder(): TreeBuilder ->scalarNode('cache_dir')->defaultValue('%kernel.cache_dir%/automapper')->end() ->scalarNode('date_time_format')->defaultValue(\DateTimeInterface::RFC3339)->end() ->booleanNode('hot_reload')->defaultValue($this->debug)->end() + ->booleanNode('map_private_properties')->defaultTrue()->end() ->booleanNode('allow_readonly_target_to_populate')->defaultFalse()->end() ->arrayNode('warmup') ->arrayPrototype() diff --git a/src/DependencyInjection/JaneAutoMapperExtension.php b/src/DependencyInjection/JaneAutoMapperExtension.php index 09786e8..b597680 100644 --- a/src/DependencyInjection/JaneAutoMapperExtension.php +++ b/src/DependencyInjection/JaneAutoMapperExtension.php @@ -45,6 +45,11 @@ public function load(array $configs, ContainerBuilder $container) $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); $loader->load('services.xml'); + $container->getDefinition(MapperGeneratorMetadataFactory::class) + ->replaceArgument(5, $config['date_time_format']) + ->replaceArgument(6, $config['map_private_properties']) + ; + $container->getDefinition(MapperGeneratorMetadataFactory::class)->replaceArgument(5, $config['date_time_format']); $container->getDefinition(FileLoader::class)->replaceArgument(2, $config['hot_reload']); $container->registerForAutoconfiguration(TransformerFactoryInterface::class)->addTag('jane_auto_mapper.transformer_factory'); diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index 07eed81..936a6a4 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -119,10 +119,5 @@ - - - - - diff --git a/tests/Fixtures/ClassWithMapToContextAttribute.php b/tests/Fixtures/ClassWithMapToContextAttribute.php new file mode 100644 index 0000000..5befb29 --- /dev/null +++ b/tests/Fixtures/ClassWithMapToContextAttribute.php @@ -0,0 +1,33 @@ +value}_{$suffix}"; + } + + public function getVirtualProperty( + #[MapToContext('prefix')] string $prefix, + #[MapToContext('suffix')] string $suffix, + ): string { + return "{$prefix}_{$this->value}_{$suffix}"; + } + + public function getPropertyWithDefaultValue( + string $someVar = 'foo', + ): string { + return $someVar; + } +} diff --git a/tests/Fixtures/ClassWithPrivateProperty.php b/tests/Fixtures/ClassWithPrivateProperty.php new file mode 100644 index 0000000..4ab1dea --- /dev/null +++ b/tests/Fixtures/ClassWithPrivateProperty.php @@ -0,0 +1,15 @@ +enum = SomeEnum::FOO; self::assertSame(['enum' => 'foo'], $autoMapper->map($dto, 'array')); } + + /** + * This test validates that PropertyInfoPass is correctly applied. + */ + public function testMapClassWithPrivateProperty(): void + { + static::bootKernel(); + $container = static::$kernel->getContainer(); + $autoMapper = $container->get(AutoMapperInterface::class); + + self::assertEquals( + new ClassWithPrivateProperty('bar'), + $autoMapper->map(['foo' => 'bar'], ClassWithPrivateProperty::class) + ); + } + + /** + * We need to test that the mapToContext attribute is correctly used, + * because this behavior is dependent of the dependency injection. + */ + public function testMapToContextAttribute(): void + { + static::bootKernel(); + $container = static::$kernel->getContainer(); + $autoMapper = $container->get(AutoMapperInterface::class); + + self::assertSame( + [ + 'value' => 'foo_bar_baz', + 'virtualProperty' => 'foo_bar_baz', + 'propertyWithDefaultValue' => 'foo', + ], + $autoMapper->map( + new ClassWithMapToContextAttribute('bar'), + 'array', + [MapperContext::MAP_TO_ACCESSOR_PARAMETER => ['suffix' => 'baz', 'prefix' => 'foo']] + ) + ); + } }