Skip to content

Commit

Permalink
Merge branch 'feature/context'
Browse files Browse the repository at this point in the history
mark-gerarts committed Nov 26, 2018
2 parents 9e8ea51 + fdf11f5 commit fcfe8ef
Showing 12 changed files with 311 additions and 28 deletions.
31 changes: 30 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -30,6 +30,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)
* [Adding context](#adding-context)
* [Misc](#misc)
* [Similar libraries](#similar-libraries)
* [See also](#see-also)
@@ -212,7 +213,7 @@ The following operations are provided:

| Name | Explanation |
| ------------- | ------------- |
| MapFrom | Maps the property from the value returned from the provided callback. Gets passed the source object and an instance of the AutoMapper. |
| MapFrom | Maps the property from the value returned from the provided callback. Gets passed the source object, an instance of the AutoMapper and optionally the [current context](#adding-context). |
| Ignore | Ignores the property. |
| MapTo | Maps the property to another class. Allows for [nested mappings](#dealing-with-nested-mappings). Supports both single values and collections. |
| FromProperty | Use this to explicitly state the source property name. |
@@ -786,6 +787,34 @@ $employee = new Employee(10, 'John', 'Doe', 1980);
$result = $mapper->map($employee, EmployeeDto::class);
```

### 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
the current context. Both the `MapFrom` and `MapTo` operations can make use of
this context to alter their behaviour.

The context argument is an array that can contain any arbitrary value. Note
that this argument isn't part of the `AutoMapperInterface` yet, since it would
break backwards compatibility. It will be added in the next major release.

```php
<?php

// This example shows how for example the current locale can be passed to alter
// the mapping behaviour.
$config->registerMapping(Employee::class, EmployeeDto::class)
->forMember(
'honorific',
function ($source, AutoMapperInterface $mapper, array $context): string {
$translationKey = "honorific.{$source->getGender()}";
return $this->translator->trans($translationKey, $context['locale']);
}
);

// Usage:
$mapper->map($employee, EmployeeDto::class, ['locale' => $request->getLocale()]);
```

### Misc

- Passing `NULL` as an argument for the source object to `map` returns `NULL`.
30 changes: 21 additions & 9 deletions src/AutoMapper.php
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@
use AutoMapperPlus\Configuration\MappingInterface;
use AutoMapperPlus\Exception\AutoMapperPlusException;
use AutoMapperPlus\Exception\UnregisteredMappingException;
use AutoMapperPlus\MappingOperation\ContextAwareOperation;
use AutoMapperPlus\MappingOperation\MapperAwareOperation;

/**
@@ -45,7 +46,7 @@ public static function initialize(callable $configurator): AutoMapperInterface
/**
* @inheritdoc
*/
public function map($source, string $destinationClass)
public function map($source, string $destinationClass, array $context = [])
{
if ($source === null) {
return null;
@@ -72,17 +73,20 @@ public function map($source, string $destinationClass)
? $mapping->getCustomConstructor()($source, $this)
: new $destinationClass;

return $this->doMap($source, $destinationObject, $mapping);
return $this->doMap($source, $destinationObject, $mapping, $context);
}

/**
* @inheritdoc
*/
public function mapMultiple($sourceCollection, string $destinationClass): array
{
public function mapMultiple(
$sourceCollection,
string $destinationClass,
array $context = []
): array {
$mappedResults = [];
foreach ($sourceCollection as $source) {
$mappedResults[] = $this->map($source, $destinationClass);
$mappedResults[] = $this->map($source, $destinationClass, $context);
}

return $mappedResults;
@@ -91,7 +95,7 @@ public function mapMultiple($sourceCollection, string $destinationClass): array
/**
* @inheritdoc
*/
public function mapToObject($source, $destination)
public function mapToObject($source, $destination, array $context = [])
{
$sourceClassName = \get_class($source);
$destinationClassName = \get_class($destination);
@@ -101,7 +105,7 @@ public function mapToObject($source, $destination)
return $this->getCustomMapper($mapping)->mapToObject($source, $destination);
}

return $this->doMap($source, $destination, $mapping);
return $this->doMap($source, $destination, $mapping, $context);
}

/**
@@ -110,18 +114,26 @@ public function mapToObject($source, $destination)
* @param $source
* @param $destination
* @param MappingInterface $mapping
* @param array $context
* @return mixed
* The destination object with mapped properties.
*/
protected function doMap($source, $destination, MappingInterface $mapping)
{
protected function doMap(
$source,
$destination,
MappingInterface $mapping,
array $context = []
) {
$propertyNames = $mapping->getTargetProperties($destination, $source);
foreach ($propertyNames as $propertyName) {
$mappingOperation = $mapping->getMappingOperationFor($propertyName);

if ($mappingOperation instanceof MapperAwareOperation) {
$mappingOperation->setMapper($this);
}
if ($mappingOperation instanceof ContextAwareOperation) {
$mappingOperation->setContext($context);
}

$mappingOperation->mapProperty(
$propertyName,
8 changes: 7 additions & 1 deletion src/AutoMapperInterface.php
Original file line number Diff line number Diff line change
@@ -16,10 +16,16 @@ interface AutoMapperInterface extends MapperInterface
* The source collection containing objects.
* @param string $targetClass
* The target classname.
* @param array $context
* See MapperInterface::map()
* @return array
* An array of mapped objects. Keys are not preserved.
*/
public function mapMultiple($sourceCollection, string $targetClass): array;
public function mapMultiple(
$sourceCollection,
string $targetClass
/**, array $context = [] */
): array;

/**
* Instantiate the mapper with a given configuration callback. The callback
12 changes: 10 additions & 2 deletions src/MapperInterface.php
Original file line number Diff line number Diff line change
@@ -19,11 +19,17 @@ interface MapperInterface
* The source object.
* @param string $targetClass
* The target classname.
* @param array $context
* An arbitrary array of values that will be passed to supporting
* mapping operations (e.g. MapFrom) to alter their behaviour based on
* the context.
* This is not explicitly required on the interface yet to preserve
* backwards compatibility, but will be added in version 2.0.
* @return mixed
* An instance of class $to.
* @throws UnregisteredMappingException
*/
public function map($source, string $targetClass);
public function map($source, string $targetClass/**, array $context = [] */);

/**
* Maps properties of object $from to an existing object $to.
@@ -32,9 +38,11 @@ public function map($source, string $targetClass);
* The source object.
* @param $destination
* The target object.
* @param array $context
* See MapperInterface::map()
* @return mixed
* $to, with properties copied from $from.
* @throws UnregisteredMappingException
*/
public function mapToObject($source, $destination);
public function mapToObject($source, $destination/**, array $context = [] */);
}
18 changes: 18 additions & 0 deletions src/MappingOperation/ContextAwareOperation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace AutoMapperPlus\MappingOperation;

/**
* Interface ContextAwareOperation
*
* @package AutoMapperPlus\MappingOperation
*/
interface ContextAwareOperation
{
/**
* Allows passing of a context array to the operation.
*
* @param array $context
*/
public function setContext(array $context = []): void;
}
23 changes: 23 additions & 0 deletions src/MappingOperation/ContextAwareTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace AutoMapperPlus\MappingOperation;

/**
* Trait ContextAwareTrait
*
* @package AutoMapperPlus\MappingOperation
*/
trait ContextAwareTrait {
/**
* @var array
*/
protected $context = [];

/**
* @inheritdoc
*/
public function setContext(array $context = []): void
{
$this->context = $context;
}
}
9 changes: 7 additions & 2 deletions src/MappingOperation/Implementations/MapFrom.php
Original file line number Diff line number Diff line change
@@ -2,6 +2,8 @@

namespace AutoMapperPlus\MappingOperation\Implementations;

use AutoMapperPlus\MappingOperation\ContextAwareOperation;
use AutoMapperPlus\MappingOperation\ContextAwareTrait;
use AutoMapperPlus\MappingOperation\DefaultMappingOperation;
use AutoMapperPlus\MappingOperation\MapperAwareOperation;
use AutoMapperPlus\MappingOperation\MapperAwareTrait;
@@ -11,9 +13,12 @@
*
* @package AutoMapperPlus\MappingOperation\Implementations
*/
class MapFrom extends DefaultMappingOperation implements MapperAwareOperation
class MapFrom extends DefaultMappingOperation implements
MapperAwareOperation,
ContextAwareOperation
{
use MapperAwareTrait;
use ContextAwareTrait;

/**
* @var callable
@@ -35,7 +40,7 @@ public function __construct(callable $valueCallback)
*/
protected function getSourceValue($source, string $propertyName)
{
return ($this->valueCallback)($source, $this->mapper);
return ($this->valueCallback)($source, $this->mapper, $this->context);
}

/**
25 changes: 21 additions & 4 deletions src/MappingOperation/Implementations/MapTo.php
Original file line number Diff line number Diff line change
@@ -2,6 +2,8 @@

namespace AutoMapperPlus\MappingOperation\Implementations;

use AutoMapperPlus\MappingOperation\ContextAwareOperation;
use AutoMapperPlus\MappingOperation\ContextAwareTrait;
use AutoMapperPlus\MappingOperation\DefaultMappingOperation;
use AutoMapperPlus\MappingOperation\MapperAwareOperation;
use AutoMapperPlus\MappingOperation\MapperAwareTrait;
@@ -13,9 +15,12 @@
*
* @package AutoMapperPlus\MappingOperation\Implementations
*/
class MapTo extends DefaultMappingOperation implements MapperAwareOperation
class MapTo extends DefaultMappingOperation implements
MapperAwareOperation,
ContextAwareOperation
{
use MapperAwareTrait;
use ContextAwareTrait;

/**
* @var string
@@ -27,20 +32,30 @@ class MapTo extends DefaultMappingOperation implements MapperAwareOperation
*/
private $sourceIsObjectArray;

/**
* @var array
*/
private $ownContext = [];

/**
* MapTo constructor.
*
* @param string $destinationClass
* @param bool $sourceIsObjectArray
* Indicates whether or not an array as source value should be treated as
* a collection of elements, or as an array representing an object.
* @param array
* $context Optional context that will be merged with the parent's
* context.
*/
public function __construct(
string $destinationClass,
bool $sourceIsObjectArray = false
bool $sourceIsObjectArray = false,
array $context = []
) {
$this->destinationClass = $destinationClass;
$this->sourceIsObjectArray = $sourceIsObjectArray;
$this->ownContext = $context;
}

/**
@@ -61,9 +76,11 @@ protected function getSourceValue($source, string $propertyName)
$this->getSourcePropertyName($propertyName)
);

$context = array_merge($this->context, $this->ownContext);

return $this->sourceIsObjectArray || !$this->isCollection($value)
? $this->mapper->map($value, $this->destinationClass)
:$this->mapper->mapMultiple($value, $this->destinationClass);
? $this->mapper->map($value, $this->destinationClass, $context)
: $this->mapper->mapMultiple($value, $this->destinationClass, $context);
}

/**
Loading

0 comments on commit fcfe8ef

Please sign in to comment.