Skip to content

Commit

Permalink
Merge pull request #1 from mark-gerarts/master
Browse files Browse the repository at this point in the history
update local fork with latest changes from master
  • Loading branch information
vasilake-v committed May 31, 2018
2 parents c32f02a + f4768bd commit 326b9eb
Show file tree
Hide file tree
Showing 17 changed files with 425 additions and 23 deletions.
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ The following operations are provided:
| 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. |
| DefaultMappingOperation | Simply transfers the property, taking into account the provided naming conventions (if there are any). |
| MapFromWithMapper | Similar to MapFrom.<br>Compared to `mapFrom`, the callback has access to a instance of `AutoMapperInterface`. Define the second callback argument of `AutoMapperInterface` type. Accessible by using <br> - `Operation::mapFromWithMapper(function($source, AutoMapperInterface $mapper){ ... })`<br>- `new mapFromWithMapper(function($source, AutoMapperInterface $mapper){ ... })`

You can use them with the same `forMember()` method. The `Operation` class can
be used for clarity.
Expand All @@ -238,13 +239,46 @@ $mapping->forMember('id', Operation::ignore());
$mapping->forMember('employee', Operation::mapTo(EmployeeDto::class));
// Explicitly state what the property name is of the source object.
$mapping->forMember('name', Operation::fromProperty('unconventially_named_property'));
// The `FromProperty` operation can be chained with `MapTo`, allowing a
// differently named property to be mapped to a class.
$mapping->forMember(
'address',
Operation::fromProperty('adres')->mapTo(Address::class)
);
```

Example of using `MapFromWithMapper`:

```php
<?php

$getColorPalette = function(SimpleXMLElement $XMLElement, AutoMapperInterface $mapper) {
/** @var SimpleXMLElement $palette */
$palette = $XMLElement->xpath('/product/specification/palette/colour');

return $mapper->mapMultiple($palette, Color::class);
};

$mapping->forMember('palette', Operation::mapFromWithMapper($getColorPalette));

// Or another Example using inline function
$mapping->forMember('palette', new MapFromWithMapper(function(SimpleXMLElement $XMLElement, AutoMapperInterface $mapper) {
/** @var SimpleXMLElement $palette */
$palette = $XMLElement->xpath('/product/specification/palette/colour');

return $mapper->mapMultiple($palette, Color::class);
}));
```

You can create your own operations by implementing the
`MappingOperationInterface`. Take a look at the
[provided implementations](https://github.com/mark-gerarts/automapper-plus/tree/master/src/MappingOperation)
for some inspiration.

If you need to have the automapper available in your operation, you can
implement the `MapperAwareInterface`, and use the `MapperAwareTrait`. The
default `MapTo` and `MapFromWithMapper` operations use these.

#### Dealing with nested mappings
Nested mappings can be registered using the `MapTo` operation. Keep in mind that the mapping for the
child class has to be registered as well.
Expand Down Expand Up @@ -756,3 +790,4 @@ Collection size: 10000
- [ ] Clean up the property checking in the Mapping::forMember() method.
- [ ] Refactor tests
- [ ] Allow setting a maximum depth, see #14
- [ ] Provide a NameResolver that accepts an array mapping, as an alternative to multiple `FromProperty`s
6 changes: 2 additions & 4 deletions src/AutoMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use AutoMapperPlus\Configuration\AutoMapperConfigInterface;
use AutoMapperPlus\Configuration\MappingInterface;
use AutoMapperPlus\Exception\UnregisteredMappingException;
use AutoMapperPlus\MappingOperation\Implementations\MapTo;
use AutoMapperPlus\MappingOperation\MapperAwareOperation;
use function Functional\map;

/**
Expand Down Expand Up @@ -106,9 +106,7 @@ protected function doMap($source, $destination, MappingInterface $mapping)
foreach ($propertyNames as $propertyName) {
$mappingOperation = $mapping->getMappingOperationFor($propertyName);

// @todo: find another solution to this hacky implementation of
// recursive mapping.
if ($mappingOperation instanceof MapTo) {
if ($mappingOperation instanceof MapperAwareOperation) {
$mappingOperation->setMapper($this);
}

Expand Down
82 changes: 81 additions & 1 deletion src/MappingOperation/Implementations/FromProperty.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

namespace AutoMapperPlus\MappingOperation\Implementations;

use AutoMapperPlus\AutoMapperInterface;
use AutoMapperPlus\Configuration\Options;
use AutoMapperPlus\MappingOperation\AlternativePropertyProvider;
use AutoMapperPlus\MappingOperation\DefaultMappingOperation;
use AutoMapperPlus\MappingOperation\MapperAwareOperation;
use AutoMapperPlus\MappingOperation\MappingOperationInterface;
use AutoMapperPlus\MappingOperation\Reversible;
use AutoMapperPlus\NameResolver\CallbackNameResolver;

/**
* Class FromProperty
Expand All @@ -15,8 +18,16 @@
*/
class FromProperty extends DefaultMappingOperation implements
AlternativePropertyProvider,
Reversible
Reversible,
// We need to be mapper aware to be able to pass the mapper to a chained
// operation.
MapperAwareOperation
{
/**
* @var MappingOperationInterface|null
*/
private $nextOperation;

/**
* @var string
*/
Expand Down Expand Up @@ -48,6 +59,22 @@ public function getAlternativePropertyName(): string
return $this->propertyName;
}

/**
* @inheritdoc
*/
public function mapProperty(string $propertyName, $source, $destination): void {
if ($this->nextOperation === null) {
parent::mapProperty($propertyName, $source, $destination);
return;
}

$this->mapPropertyWithNextOperation(
$propertyName,
$source,
$destination
);
}

/**
* @inheritdoc
*/
Expand All @@ -71,4 +98,57 @@ public function getReverseTargetPropertyName
{
return $this->propertyName;
}

/**
* Chain a MapTo operation, making the MapTo use this operation's property
* name instead.
*
* Note: because MapTo is not reversible, the MapTo part gets lost when
* reversing the mapping.
*
* @todo: extend to other operations, or maybe a __call?
*
* @param string $class
* @return FromProperty
*/
public function mapTo(string $class): FromProperty
{
$this->nextOperation = new MapTo($class);
return $this;
}

/**
* @param string $propertyName
* @param $source
* @param $destination
*/
protected function mapPropertyWithNextOperation(
string $propertyName,
$source,
$destination
): void
{
// We have to make the overridden property available to the next
// operation. To do this, we create a "one-time use" name resolver
// to pass to the operation.
$options = clone $this->options;
$options->setNameResolver(new CallbackNameResolver(function () {
return $this->propertyName;
}));
$this->nextOperation->setOptions($options);

// The chained operation will now use the property name assigned to
// FromProperty, so we can go ahead and call it.
$this->nextOperation->mapProperty($propertyName, $source, $destination);
}

/**
* @inheritdoc
*/
public function setMapper(AutoMapperInterface $mapper): void
{
if ($this->nextOperation instanceof MapperAwareOperation) {
$this->nextOperation->setMapper($mapper);
}
}
}
2 changes: 1 addition & 1 deletion src/MappingOperation/Implementations/MapFrom.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class MapFrom extends DefaultMappingOperation
/**
* @var callable
*/
private $valueCallback;
protected $valueCallback;

/**
* MapFrom constructor.
Expand Down
30 changes: 30 additions & 0 deletions src/MappingOperation/Implementations/MapFromWithMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php
/**
* Created by PhpStorm.
* User: Veaceslav Vasilache <[email protected]>
* Date: 3/14/18
* Time: 1:16 PM
*/

namespace AutoMapperPlus\MappingOperation\Implementations;

use AutoMapperPlus\MappingOperation\MapperAwareOperation;
use AutoMapperPlus\MappingOperation\MapperAwareTrait;

/**
* Class MapFromWithMapper
*
* @package AutoMapperPlus\MappingOperation\Implementations
*/
class MapFromWithMapper extends MapFrom implements MapperAwareOperation
{
use MapperAwareTrait;

/**
* @inheritdoc
*/
protected function getSourceValue($source, string $propertyName)
{
return ($this->valueCallback)($source, $this->mapper);
}
}
25 changes: 9 additions & 16 deletions src/MappingOperation/Implementations/MapTo.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

namespace AutoMapperPlus\MappingOperation\Implementations;

use AutoMapperPlus\AutoMapperInterface;
use AutoMapperPlus\MappingOperation\DefaultMappingOperation;
use AutoMapperPlus\MappingOperation\MapperAwareOperation;
use AutoMapperPlus\MappingOperation\MapperAwareTrait;

/**
* Class MapTo.
Expand All @@ -12,18 +13,15 @@
*
* @package AutoMapperPlus\MappingOperation\Implementations
*/
class MapTo extends DefaultMappingOperation
class MapTo extends DefaultMappingOperation implements MapperAwareOperation
{
use MapperAwareTrait;

/**
* @var string
*/
private $destinationClass;

/**
* @var AutoMapperInterface
*/
private $mapper;

/**
* MapTo constructor.
*
Expand All @@ -42,20 +40,15 @@ public function getDestinationClass(): string
return $this->destinationClass;
}

/**
* @param AutoMapperInterface $mapper
*/
public function setMapper(AutoMapperInterface $mapper)
{
$this->mapper = $mapper;
}

/**
* @inheritdoc
*/
protected function getSourceValue($source, string $propertyName)
{
$value = $this->getPropertyAccessor()->getProperty($source, $propertyName);
$value = $this->getPropertyAccessor()->getProperty(
$source,
$this->getSourcePropertyName($propertyName)
);

return $this->isCollection($value)
? $this->mapper->mapMultiple($value, $this->destinationClass)
Expand Down
17 changes: 17 additions & 0 deletions src/MappingOperation/MapperAwareOperation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace AutoMapperPlus\MappingOperation;

use AutoMapperPlus\AutoMapperInterface;

/**
* Interface MapperAwareOperation
*
* @package AutoMapperPlus\MappingOperation
*/
interface MapperAwareOperation {
/**
* @param AutoMapperInterface $mapper
*/
public function setMapper(AutoMapperInterface $mapper): void;
}
26 changes: 26 additions & 0 deletions src/MappingOperation/MapperAwareTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace AutoMapperPlus\MappingOperation;

use AutoMapperPlus\AutoMapperInterface;

/**
* Trait MapperAwareTrait
*
* @package AutoMapperPlus\MappingOperation
*/
trait MapperAwareTrait
{
/**
* @var AutoMapperInterface
*/
protected $mapper;

/**
* @param AutoMapperInterface $mapper
*/
public function setMapper(AutoMapperInterface $mapper): void
{
$this->mapper = $mapper;
}
}
17 changes: 17 additions & 0 deletions src/MappingOperation/Operation.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use AutoMapperPlus\MappingOperation\Implementations\FromProperty;
use AutoMapperPlus\MappingOperation\Implementations\Ignore;
use AutoMapperPlus\MappingOperation\Implementations\MapFrom;
use AutoMapperPlus\MappingOperation\Implementations\MapFromWithMapper;
use AutoMapperPlus\MappingOperation\Implementations\MapTo;

/**
Expand All @@ -28,6 +29,22 @@ public static function mapFrom(callable $valueCallback): MapFrom
return new MapFrom($valueCallback);
}

/**
* Set a property's value from callback, callback should contain 2 parameters
*
* @param callable $valueCallback
* Callback definition:
*
* function(AutoMapperInterface, mixed){
* }
* @return MapFromWithMapper
*/
public static function mapFromWithMapper(callable $valueCallback): MapFromWithMapper
{
return new MapFromWithMapper($valueCallback);
}

/**
* Ignore a property.
*
Expand Down
Loading

0 comments on commit 326b9eb

Please sign in to comment.