From 362d49bd11be3d9bd9b0d73bacd2f6af34cc7631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Alvergnat?= Date: Thu, 8 Aug 2019 15:34:39 +0200 Subject: [PATCH] Add middleware feature --- README.md | 52 +++- src/AutoMapper.php | 122 +++++++++- src/Configuration/AutoMapperConfig.php | 62 ++++- .../AutoMapperConfigInterface.php | 22 ++ src/Middleware/MapperMiddleware.php | 47 ++++ src/Middleware/Middleware.php | 38 +++ src/Middleware/PropertyMiddleware.php | 58 +++++ .../Middleware/AnwserToUniverseMiddleware.php | 35 +++ test/Middleware/AppendMapperMiddleware.php | 31 +++ test/Middleware/AppendPropertyMiddleware.php | 32 +++ test/Middleware/BeforeMapperMiddleware.php | 22 ++ test/Middleware/BeforePropertyMiddleware.php | 25 ++ test/Middleware/NoopMapperMiddleware.php | 29 +++ test/Middleware/NoopPropertyMiddleware.php | 30 +++ test/Middleware/SkipMapperMiddleware.php | 22 ++ test/Middleware/SkipPropertyMiddleware.php | 23 ++ test/Middleware/ValueMapperMiddleware.php | 31 +++ test/Middleware/ValuePropertyMiddleware.php | 32 +++ test/Models/Employee/Employee.php | 2 +- .../SimpleProperties/CompleteSource.php | 21 ++ test/Scenarios/MiddlewareTest.php | 222 ++++++++++++++++++ 21 files changed, 941 insertions(+), 17 deletions(-) create mode 100644 src/Middleware/MapperMiddleware.php create mode 100644 src/Middleware/Middleware.php create mode 100644 src/Middleware/PropertyMiddleware.php create mode 100644 test/Middleware/AnwserToUniverseMiddleware.php create mode 100644 test/Middleware/AppendMapperMiddleware.php create mode 100644 test/Middleware/AppendPropertyMiddleware.php create mode 100644 test/Middleware/BeforeMapperMiddleware.php create mode 100644 test/Middleware/BeforePropertyMiddleware.php create mode 100644 test/Middleware/NoopMapperMiddleware.php create mode 100644 test/Middleware/NoopPropertyMiddleware.php create mode 100644 test/Middleware/SkipMapperMiddleware.php create mode 100644 test/Middleware/SkipPropertyMiddleware.php create mode 100644 test/Middleware/ValueMapperMiddleware.php create mode 100644 test/Middleware/ValuePropertyMiddleware.php create mode 100644 test/Models/SimpleProperties/CompleteSource.php create mode 100644 test/Scenarios/MiddlewareTest.php diff --git a/README.md b/README.md index 10cdc87..a53a158 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ Transfers data from one object to another, allowing custom mapping operations. * [The concept of object crates](#the-concept-of-object-crates) * [Mapping with arrays](#mapping-with-arrays) * [Using a custom mapper](#using-a-custom-mapper) + * [Using middlewares](#using-middlewares) * [Adding context](#adding-context) * [Misc](#misc) * [Similar libraries](#similar-libraries) @@ -804,6 +805,55 @@ $employee = new Employee(10, 'John', 'Doe', 1980); $result = $mapper->map($employee, EmployeeDto::class); ``` +### Using middlewares +You can register middlewares to customize how automapper works internally and define +global behaviors. + +The following example will set 42 to any `id` property that would have been `null`. + +```php +getOptions()->getPropertyReader()->getProperty($destination, $propertyName); + if ($defaultValue === NULL) { + $mapping->getOptions()->getPropertyWriter()->setProperty($destination, $propertyName, 42); + } + } +} + +$config->registerMiddlewares(new AnwserToUniverseMiddleware()); +$config->registerMapping(Employee::class, EmployeeDto::class); +$mapper = new AutoMapper($config); + +// The AutoMapper can now be used as usual, but your middleware will intercept some property mappings. +$employee = new Employee(NULL, 'John', 'Doe', 1980); +$result = $mapper->map($employee, EmployeeDto::class); +echo $result->id; // => 42 +``` + +Middleware feature open up many extension capabilities, feel free to check PHPDocs +from [PropertyMiddleware](./src/Middleware/PropertyMiddleware.php) and +[MapperMiddleware](./src/Middleware/MapperMiddleware.php) interfaces for details. + ### Adding context Sometimes a mapping should behave differently based on the context. It is therefore possible to pass a third argument to the map methods to describe @@ -915,7 +965,7 @@ where needed, without needing to change the code that uses the mapper. - [ ] Allow setting a maximum depth, see #14 - [ ] Provide a NameResolver that accepts an array mapping, as an alternative to multiple `FromProperty`s - [ ] Make use of a decorated Symfony's `PropertyAccessor` (see [#16](https://github.com/mark-gerarts/automapper-plus/issues/16)) -- [ ] Allow adding of middleware to the mapper +- [x] Allow adding of middleware to the mapper - [ ] Allow mapping *to* array *[Version 2](https://github.com/mark-gerarts/automapper-plus/tree/2.0) is in the works, check there for new features as well* diff --git a/src/AutoMapper.php b/src/AutoMapper.php index 06a73be..0f9759b 100644 --- a/src/AutoMapper.php +++ b/src/AutoMapper.php @@ -11,6 +11,10 @@ use AutoMapperPlus\Exception\UnsupportedSourceTypeException; use AutoMapperPlus\MappingOperation\ContextAwareOperation; use AutoMapperPlus\MappingOperation\MapperAwareOperation; +use AutoMapperPlus\MappingOperation\MappingOperationInterface; +use AutoMapperPlus\Middleware\MapperMiddleware; +use AutoMapperPlus\Middleware\Middleware; +use AutoMapperPlus\Middleware\PropertyMiddleware; /** * Class AutoMapper @@ -164,7 +168,7 @@ public function mapToObject($source, $destination, array $context = []) } /** - * Performs the actual transferring of properties. + * Performs the actual transferring of properties, involving all matching mapper and property middleware. * * @param $source * @param $destination @@ -179,6 +183,44 @@ protected function doMap( MappingInterface $mapping, array $context = [] ) { + $mapperMiddlewares = $this->getMapperMiddlewares($source, $destination, $mapping, $context); + foreach ($mapperMiddlewares[Middleware::BEFORE] as $middleware) { + $middleware->map($source, $destination, $mapping, $context); + } + + $overrideMiddlewares = $mapperMiddlewares[Middleware::OVERRIDE]; + if ($overrideMiddlewares) { + foreach ($overrideMiddlewares as $middleware) { + $middleware->map($source, $destination, $mapping, $context); + } + } else { + $this->doMapDefault($source, $destination, $mapping, $context); + } + + foreach ($mapperMiddlewares[Middleware::AFTER] as $middleware) { + $middleware->map($source, $destination, $mapping, $context); + } + + return $destination; + } + + /** + * Performs the actual default transferring of properties, involving all registered property middleware. + * + * @param $source + * @param $destination + * @param MappingInterface $mapping + * @param array $context + * @return mixed + * The destination object with mapped properties. + */ + protected function doMapDefault( + $source, + $destination, + MappingInterface $mapping, + array $context = [] + ) + { $propertyNames = $mapping->getTargetProperties($destination, $source); foreach ($propertyNames as $propertyName) { $mappingOperation = $mapping->getMappingOperationFor($propertyName); @@ -190,14 +232,28 @@ protected function doMap( $mappingOperation->setContext($context); } - $mappingOperation->mapProperty( - $propertyName, - $source, - $destination - ); - } + $propertyMiddlewares = $this->getPropertyMiddlewares($propertyName, $source, $destination, $mapping, $mappingOperation, $context); + foreach ($propertyMiddlewares[Middleware::BEFORE] as $middleware) { + $middleware->mapProperty($propertyName, $source, $destination, $mapping, $mappingOperation, $context); + } - return $destination; + $overrideMiddlewares = $propertyMiddlewares[Middleware::OVERRIDE]; + if ($overrideMiddlewares) { + foreach ($overrideMiddlewares as $middleware) { + $middleware->mapProperty($propertyName, $source, $destination, $mapping, $mappingOperation, $context); + } + } else { + $mappingOperation->mapProperty( + $propertyName, + $source, + $destination + ); + } + + foreach ($propertyMiddlewares[Middleware::AFTER] as $middleware) { + $middleware->mapProperty($propertyName, $source, $destination, $mapping, $mappingOperation, $context); + } + } } /** @@ -248,4 +304,54 @@ private function getCustomMapper(MappingInterface $mapping): ?MapperInterface return $customMapper; } + + /** + * @param $source + * @param $destination + * @param string $propertyName + * @param MappingInterface $mapping + * @param MappingOperationInterface $mappingOperation + * @param array $context + * @return PropertyMiddleware[][] + */ + private function getPropertyMiddlewares( + $propertyName, + $source, + $destination, + MappingInterface $mapping, + MappingOperationInterface $mappingOperation, + array $context = []): array + { + $propertyMiddleware = [Middleware::AFTER => [], Middleware::OVERRIDE => NULL, Middleware::BEFORE => []]; + foreach ($this->getConfiguration()->getPropertyMiddlewares() as $middleware) { + $supports = intval($middleware->supportsMapProperty($propertyName, $source, $destination, $mapping, $mappingOperation, $context)); + if ($supports != Middleware::SKIP) { + $propertyMiddleware[$supports][] = $middleware; + } + } + return $propertyMiddleware; + } + + /** + * @param $source + * @param $destination + * @param MappingInterface $mapping + * @param array $context + * @return MapperMiddleware[][] + */ + private function getMapperMiddlewares( + $source, + $destination, + MappingInterface $mapping, + array $context = []): array + { + $mapperMiddlewares = [Middleware::AFTER => [], Middleware::OVERRIDE => [], Middleware::BEFORE => []]; + foreach ($this->getConfiguration()->getMapperMiddlewares() as $middleware) { + $supports = intval($middleware->supportsMap($source, $destination, $mapping, $context)); + if ($supports != Middleware::SKIP) { + $mapperMiddlewares[$supports][] = $middleware; + } + } + return $mapperMiddlewares; + } } diff --git a/src/Configuration/AutoMapperConfig.php b/src/Configuration/AutoMapperConfig.php index e982f1c..50a1eab 100644 --- a/src/Configuration/AutoMapperConfig.php +++ b/src/Configuration/AutoMapperConfig.php @@ -2,6 +2,10 @@ namespace AutoMapperPlus\Configuration; +use AutoMapperPlus\Middleware\MapperMiddleware; +use AutoMapperPlus\Middleware\Middleware; +use AutoMapperPlus\Middleware\PropertyMiddleware; + /** * Class AutoMapperConfig * @@ -14,6 +18,16 @@ class AutoMapperConfig implements AutoMapperConfigInterface */ private $mappings = []; + /** + * @var MapperMiddleware[] + */ + private $mapperMiddlewares = []; + + /** + * @var PropertyMiddleware[] + */ + private $propertyMiddlewares = []; + /** * @var Options */ @@ -38,7 +52,8 @@ public function __construct(callable $configurator = null) public function hasMappingFor( string $sourceClassName, string $destinationClassName - ): bool { + ): bool + { $mapping = $this->getMappingFor( $sourceClassName, $destinationClassName @@ -53,7 +68,8 @@ public function hasMappingFor( public function getMappingFor( string $sourceClassName, string $destinationClassName - ): ?MappingInterface { + ): ?MappingInterface + { // Check for an exact match before we try parent classes. foreach ($this->mappings as $mapping) { $isExactMatch = $mapping->getSourceClassName() === $sourceClassName @@ -120,11 +136,12 @@ protected function getMostSpecificCandidate( array $candidates, string $sourceClassName, string $destinationClassName - ): ?MappingInterface { + ): ?MappingInterface + { $lowestDistance = PHP_INT_MAX; $selectedCandidate = null; /** @var MappingInterface $candidate */ - foreach($candidates as $candidate) { + foreach ($candidates as $candidate) { $sourceDistance = $this->getClassDistance( $sourceClassName, $candidate->getSourceClassName() @@ -154,14 +171,15 @@ protected function getMostSpecificCandidate( protected function getClassDistance( string $childClass, string $parentClass - ): int { + ): int + { if ($childClass === $parentClass) { return 0; } $result = 0; $childParents = class_parents($childClass, true); - foreach($childParents as $childParent) { + foreach ($childParents as $childParent) { $result++; if ($childParent === $parentClass) { return $result; @@ -194,7 +212,8 @@ protected function getClassDistance( public function registerMapping( string $sourceClassName, string $destinationClassName - ): MappingInterface { + ): MappingInterface + { $mapping = new Mapping( $sourceClassName, $destinationClassName, @@ -205,6 +224,19 @@ public function registerMapping( return $mapping; } + public function registerMiddlewares(Middleware ...$middlewares): void + { + foreach ($middlewares as $middleware) { + if ($middleware instanceof MapperMiddleware) { + $this->mapperMiddlewares[] = $middleware; + } + if ($middleware instanceof PropertyMiddleware) { + $this->propertyMiddlewares[] = $middleware; + } + } + } + + /** * @inheritdoc */ @@ -212,4 +244,20 @@ public function getOptions(): Options { return $this->options; } + + /** + * @inheritdoc + */ + public function getMapperMiddlewares() + { + return $this->mapperMiddlewares; + } + + /** + * @inheritdoc + */ + public function getPropertyMiddlewares() + { + return $this->propertyMiddlewares; + } } diff --git a/src/Configuration/AutoMapperConfigInterface.php b/src/Configuration/AutoMapperConfigInterface.php index 7eb6d07..8523cb3 100644 --- a/src/Configuration/AutoMapperConfigInterface.php +++ b/src/Configuration/AutoMapperConfigInterface.php @@ -2,6 +2,10 @@ namespace AutoMapperPlus\Configuration; +use AutoMapperPlus\Middleware\MapperMiddleware; +use AutoMapperPlus\Middleware\Middleware; +use AutoMapperPlus\Middleware\PropertyMiddleware; + /** * Interface AutoMapperConfigInterface * @@ -47,8 +51,26 @@ public function registerMapping( string $destinationClassName ): MappingInterface; + /** + * Register middlewares. + * + * @param Middleware ...$middlewares + * @return void + */ + public function registerMiddlewares(Middleware ...$middlewares): void; + /** * @return Options */ public function getOptions(): Options; + + /** + * @return MapperMiddleware[] + */ + public function getMapperMiddlewares(); + + /** + * @return PropertyMiddleware[] + */ + public function getPropertyMiddlewares(); } diff --git a/src/Middleware/MapperMiddleware.php b/src/Middleware/MapperMiddleware.php new file mode 100644 index 0000000..a1cc7e9 --- /dev/null +++ b/src/Middleware/MapperMiddleware.php @@ -0,0 +1,47 @@ +getOptions()->getPropertyReader()->getProperty($destination, $propertyName); + if ($defaultValue === NULL) { + $mapping->getOptions()->getPropertyWriter()->setProperty($destination, $propertyName, 42); + } + } +} \ No newline at end of file diff --git a/test/Middleware/AppendMapperMiddleware.php b/test/Middleware/AppendMapperMiddleware.php new file mode 100644 index 0000000..7ad5897 --- /dev/null +++ b/test/Middleware/AppendMapperMiddleware.php @@ -0,0 +1,31 @@ +append = $append; + $this->supportsValue = $supportsValue; + } + + public function supportsMap($source, $destination, MappingInterface $mapping, array $context = []) + { + return $this->supportsValue; + } + + public function map($source, $destination, MappingInterface $mapping, array $context = []) + { + $destination->name = $destination->name . $this->append; + } +} \ No newline at end of file diff --git a/test/Middleware/AppendPropertyMiddleware.php b/test/Middleware/AppendPropertyMiddleware.php new file mode 100644 index 0000000..ba05b6e --- /dev/null +++ b/test/Middleware/AppendPropertyMiddleware.php @@ -0,0 +1,32 @@ +append = $append; + $this->supportsValue = $supportsValue; + } + + public function supportsMapProperty($propertyName, $source, $destination, MappingInterface $mapping, MappingOperationInterface $operation, array $context = []) + { + return $propertyName == 'name' ? $this->supportsValue : Middleware::SKIP; + } + + public function mapProperty($propertyName, $source, $destination, MappingInterface $mapping, MappingOperationInterface $operation, array $context = []) + { + $destination->{$propertyName} = $destination->{$propertyName} . $this->append; + } +} \ No newline at end of file diff --git a/test/Middleware/BeforeMapperMiddleware.php b/test/Middleware/BeforeMapperMiddleware.php new file mode 100644 index 0000000..fbc672e --- /dev/null +++ b/test/Middleware/BeforeMapperMiddleware.php @@ -0,0 +1,22 @@ +name = 'This should never appear'; + } +} \ No newline at end of file diff --git a/test/Middleware/BeforePropertyMiddleware.php b/test/Middleware/BeforePropertyMiddleware.php new file mode 100644 index 0000000..5e8ab44 --- /dev/null +++ b/test/Middleware/BeforePropertyMiddleware.php @@ -0,0 +1,25 @@ +{$propertyName} = 'This should never appear'; + } +} \ No newline at end of file diff --git a/test/Middleware/NoopMapperMiddleware.php b/test/Middleware/NoopMapperMiddleware.php new file mode 100644 index 0000000..af4ca5c --- /dev/null +++ b/test/Middleware/NoopMapperMiddleware.php @@ -0,0 +1,29 @@ +supportsValue = $supportsValue; + } + + public function supportsMap($source, $destination, MappingInterface $mapping, array $context = []) + { + return $this->supportsValue; + } + + public function map($source, $destination, MappingInterface $mapping, array $context = []) + { + //NOOP + } +} \ No newline at end of file diff --git a/test/Middleware/NoopPropertyMiddleware.php b/test/Middleware/NoopPropertyMiddleware.php new file mode 100644 index 0000000..2bc297b --- /dev/null +++ b/test/Middleware/NoopPropertyMiddleware.php @@ -0,0 +1,30 @@ +supportsValue = $supportsValue; + } + + public function supportsMapProperty($propertyName, $source, $destination, MappingInterface $mapping, MappingOperationInterface $operation, array $context = []) + { + return $propertyName == 'name' ? $this->supportsValue : false; + } + + public function mapProperty($propertyName, $source, $destination, MappingInterface $mapping, MappingOperationInterface $operation, array $context = []) + { + //NOOP + } +} \ No newline at end of file diff --git a/test/Middleware/SkipMapperMiddleware.php b/test/Middleware/SkipMapperMiddleware.php new file mode 100644 index 0000000..7d77d05 --- /dev/null +++ b/test/Middleware/SkipMapperMiddleware.php @@ -0,0 +1,22 @@ +name = 'This should never happen'; + } +} \ No newline at end of file diff --git a/test/Middleware/SkipPropertyMiddleware.php b/test/Middleware/SkipPropertyMiddleware.php new file mode 100644 index 0000000..83289c8 --- /dev/null +++ b/test/Middleware/SkipPropertyMiddleware.php @@ -0,0 +1,23 @@ +{$propertyName} = 'This should happen unless it is name property'; + } +} \ No newline at end of file diff --git a/test/Middleware/ValueMapperMiddleware.php b/test/Middleware/ValueMapperMiddleware.php new file mode 100644 index 0000000..dcd6441 --- /dev/null +++ b/test/Middleware/ValueMapperMiddleware.php @@ -0,0 +1,31 @@ +value = $value; + $this->supportsValue = $supportsValue; + } + + public function supportsMap($source, $destination, MappingInterface $mapping, array $context = []) + { + return $this->supportsValue; + } + + public function map($source, $destination, MappingInterface $mapping, array $context = []) + { + $destination->name = $this->value; + } +} \ No newline at end of file diff --git a/test/Middleware/ValuePropertyMiddleware.php b/test/Middleware/ValuePropertyMiddleware.php new file mode 100644 index 0000000..1bebf9b --- /dev/null +++ b/test/Middleware/ValuePropertyMiddleware.php @@ -0,0 +1,32 @@ +value = $value; + $this->supportsValue = $supportsValue; + } + + public function supportsMapProperty($propertyName, $source, $destination, MappingInterface $mapping, MappingOperationInterface $operation, array $context = []) + { + return $propertyName == 'name' ? $this->supportsValue : false; + } + + public function mapProperty($propertyName, $source, $destination, MappingInterface $mapping, MappingOperationInterface $operation, array $context = []) + { + $destination->name = $this->value; + } +} \ No newline at end of file diff --git a/test/Models/Employee/Employee.php b/test/Models/Employee/Employee.php index 5ca526f..1edb393 100644 --- a/test/Models/Employee/Employee.php +++ b/test/Models/Employee/Employee.php @@ -15,7 +15,7 @@ class Employee private $birthYear; private $address; - function __construct(int $id, string $firstName, string $lastName, int $birthYear, $address = null) + function __construct(?int $id, string $firstName, string $lastName, int $birthYear, $address = null) { $this->id = $id; $this->firstName = $firstName; diff --git a/test/Models/SimpleProperties/CompleteSource.php b/test/Models/SimpleProperties/CompleteSource.php new file mode 100644 index 0000000..9b1a916 --- /dev/null +++ b/test/Models/SimpleProperties/CompleteSource.php @@ -0,0 +1,21 @@ +name = $name; + $this->anotherProperty = $anotherProperty; + } +} diff --git a/test/Scenarios/MiddlewareTest.php b/test/Scenarios/MiddlewareTest.php new file mode 100644 index 0000000..903d5da --- /dev/null +++ b/test/Scenarios/MiddlewareTest.php @@ -0,0 +1,222 @@ +registerMiddlewares(new ValueMapperMiddleware()); + $config->registerMapping(Source::class, Destination::class); + $mapper = new AutoMapper($config); + $source = new Source('a name'); + + /** @var Destination $result */ + $result = $mapper->map($source, Destination::class); + + $this->assertEquals('mapper middleware value', $result->name); + } + + public function testValuePropertyMiddleware() + { + $config = new AutoMapperConfig(); + $config->registerMiddlewares(new ValuePropertyMiddleware()); + $config->registerMapping(CompleteSource::class, Destination::class); + $mapper = new AutoMapper($config); + $source = new CompleteSource('a name', 'another property'); + + /** @var Destination $result */ + $result = $mapper->map($source, Destination::class); + + $this->assertEquals('property middleware value', $result->name); + $this->assertEquals('another property', $result->anotherProperty); + } + + public function testAppendMapperMiddleware() + { + $config = new AutoMapperConfig(); + $config->registerMiddlewares(new AppendMapperMiddleware()); + $config->registerMapping(Source::class, Destination::class); + $mapper = new AutoMapper($config); + $source = new Source('a name'); + + /** @var Destination $result */ + $result = $mapper->map($source, Destination::class); + + $this->assertEquals('a name (append)', $result->name); + } + + public function testAppendPropertyMiddleware() + { + $config = new AutoMapperConfig(); + $config->registerMiddlewares(new AppendPropertyMiddleware()); + $config->registerMapping(CompleteSource::class, Destination::class); + $mapper = new AutoMapper($config); + $source = new CompleteSource('a name', 'another property'); + + /** @var Destination $result */ + $result = $mapper->map($source, Destination::class); + + $this->assertEquals('a name (append)', $result->name); + $this->assertEquals('another property', $result->anotherProperty); + } + + public function testBeforeNameMapperMiddleware() + { + $config = new AutoMapperConfig(); + $config->registerMiddlewares(new BeforeMapperMiddleware()); + $config->registerMapping(Source::class, Destination::class); + $mapper = new AutoMapper($config); + $source = new Source('a name'); + + /** @var Destination $result */ + $result = $mapper->map($source, Destination::class); + + $this->assertEquals('a name', $result->name); + } + + public function testBeforeNamePropertyMiddleware() + { + $config = new AutoMapperConfig(); + $config->registerMiddlewares(new BeforePropertyMiddleware()); + $config->registerMapping(CompleteSource::class, Destination::class); + $mapper = new AutoMapper($config); + $source = new CompleteSource('a name', 'another property'); + + /** @var Destination $result */ + $result = $mapper->map($source, Destination::class); + + $this->assertEquals('a name', $result->name); + $this->assertEquals('another property', $result->anotherProperty); + } + + public function testSkipMapperMiddleware() + { + $config = new AutoMapperConfig(); + $config->registerMiddlewares(new SkipMapperMiddleware()); + $config->registerMapping(Source::class, Destination::class); + $mapper = new AutoMapper($config); + $source = new Source('a name'); + + /** @var Destination $result */ + $result = $mapper->map($source, Destination::class); + + $this->assertEquals('a name', $result->name); + } + + public function testSkipPropertyMiddleware() + { + $config = new AutoMapperConfig(); + $config->registerMiddlewares(new SkipPropertyMiddleware()); + $config->registerMapping(CompleteSource::class, Destination::class); + $mapper = new AutoMapper($config); + $source = new CompleteSource('a name', 'another property'); + + /** @var Destination $result */ + $result = $mapper->map($source, Destination::class); + + $this->assertEquals('a name', $result->name); + $this->assertEquals('This should happen unless it is name property', $result->anotherProperty); + } + + public function testNoopMapperMiddleware() + { + $config = new AutoMapperConfig(); + $config->registerMiddlewares(new NoopMapperMiddleware()); + $config->registerMapping(Source::class, Destination::class); + $mapper = new AutoMapper($config); + $source = new Source('a name'); + + /** @var Destination $result */ + $result = $mapper->map($source, Destination::class); + + $this->assertNull($result->name); + } + + public function testNoopPropertyMiddleware() + { + $config = new AutoMapperConfig(); + $config->registerMiddlewares(new NoopPropertyMiddleware()); + $config->registerMapping(CompleteSource::class, Destination::class); + $mapper = new AutoMapper($config); + $source = new CompleteSource('a name', 'another property'); + + /** @var Destination $result */ + $result = $mapper->map($source, Destination::class); + + $this->assertNull($result->name); + $this->assertEquals('another property', $result->anotherProperty); + } + + public function testManyMiddlewares() + { + $config = new AutoMapperConfig(); + $config->registerMiddlewares( + new AppendPropertyMiddleware('mapper middleware value', Middleware::OVERRIDE), + new AppendMapperMiddleware('[before]', Middleware::BEFORE), + new AppendPropertyMiddleware('[before-property]', Middleware::BEFORE), + new AppendMapperMiddleware('[after]'), + new AppendPropertyMiddleware('[after-property]') + ); + $config->registerMapping(CompleteSource::class, Destination::class); + $mapper = new AutoMapper($config); + $source = new CompleteSource('a name', 'another property'); + + /** @var Destination $result */ + $result = $mapper->map($source, Destination::class); + + $this->assertEquals('[before][before-property]mapper middleware value[after-property][after]', $result->name); + $this->assertEquals('another property', $result->anotherProperty); + } + + public function testAnswerToUniverse() + { + $config = new AutoMapperConfig(); + $config->registerMiddlewares(new AnwserToUniverseMiddleware()); + $config->registerMapping(Employee::class, EmployeeDto::class); + $config->registerMapping( + \AutoMapperPlus\Test\Models\SimilarPropertyNames\Source::class, + \AutoMapperPlus\Test\Models\SimilarPropertyNames\Destination::class + ); + + $mapper = new AutoMapper($config); + $source1 = new Employee(NULL, 'John', 'Doe', 1980); + $source2 = new \AutoMapperPlus\Test\Models\SimilarPropertyNames\Source(NULL, NULL); + + /** @var EmployeeDto $result1 */ + $result1 = $mapper->map($source1, EmployeeDto::class); + $this->assertEquals(42, $result1->id); + $this->assertEquals('John', $result1->firstName); + $this->assertEquals('Doe', $result1->lastName); + + /** + * @var \AutoMapperPlus\Test\Models\SimilarPropertyNames\Destination $result2 + */ + $result2 = $mapper->map($source2, \AutoMapperPlus\Test\Models\SimilarPropertyNames\Destination::class); + $this->assertEquals(42, $result2->id); + $this->assertNull($result2->second_id); + + } +}