Inspired by http://github.com/AutoMapper/AutoMapper, this Bundle provides a object to object mapper.
Add to your /deps
file :
[BCCAutoMapperBundle]
git=http://github.com/michelsalib/BCCAutoMapperBundle.git
target=/bundles/BCC/AutoMapperBundle
And make a php bin/vendors install
.
<?php
// app/autoload.php
$loader->registerNamespaces(array(
'BCC' => __DIR__.'/../vendor/bundles',
// your other namespaces
));
<?php
// app/AppKernel.php
public function registerBundles()
{
return array(
// ...
new BCC\AutoMapperBundle\BCCAutoMapperBundle(),
// ...
);
}
Giving a source and a destination object:
<?php
namespace My;
class SourcePost {
public $name;
public $description;
/**
* @var SourceAuthor
*/
public $author;
}
class SourceAuthor {
public $name;
}
class DestinationPost {
public $title;
public $description;
public $author;
}
THe default map will automatically associate members that have the same name. It will automatically use public properties or look for getters.
You can create a default map and map object this way:
<?php
// get mapper
$mapper = $container->get('bcc_auto_mapper.mapper');
// create default map
$mapper->createMap('My\SourcePost', 'My\DestinationPost');
// create objects
$source = new SourcePost();
$source->description = 'Symfony2 developer';
$destination = new DestinationPost();
// map
$mapper->map($source, $destination);
echo destination->description; // outputs 'Symfony2 developer'
On the previous example the fields name
and title
did not match. You can route members this way:
<?php
// get mapper
$mapper = $container->get('bcc_auto_mapper.mapper');
// create default map and route members
$mapper->createMap('My\SourcePost', 'My\DestinationPost')
->route('title', 'name');
// create objects
$source = new SourcePost();
$source->name = 'AutoMapper Bundle';
$destination = new DestinationPost();
// map
$mapper->map($source, $destination);
echo destination->title; // outputs 'AutoMapper Bundle'
Note that if title
or name
is private, it will try to use getter and setter to route the member.
If you need some extra computation when mapping a member, you can provide a closure that will handle a specific member:
<?php
use BCC\AutoMapperBundle\Mapper\FieldAccessor\Closure;
// get mapper
$mapper = $container->get('bcc_auto_mapper.mapper');
// create default map and override members
$mapper->createMap('My\SourcePost', 'My\DestinationPost')
->forMember('title', new Closure(function(SourcePost $source){
return \strtoupper($source->name);
}));
// create objects
$source = new SourcePost();
$source->name = 'AutoMapper Bundle';
$destination = new DestinationPost();
// map
$mapper->map($source, $destination);
echo destination->title; // outputs 'AUTOMAPPER BUNDLE'
You can map the author->name member this way:
<?php
// get mapper
$mapper = $container->get('bcc_auto_mapper.mapper');
// create default map and override members
$mapper->createMap('My\SourcePost', 'My\DestinationPost')
->route('author', 'author.name');
// create objects
$source = new SourcePost();
$source->author = new SourceAuthor();
$source->author->name = 'Michel';
$destination = new DestinationPost();
$mapper = new Mapper();
// map
$mapper->map($source, $destination);
echo destination->author; // outputs 'Michel'
Note that if there are private members, it will try to use getter and setter to route the member.
You can map a specific member to a constant:
<?php
use BCC\AutoMapperBundle\Mapper\FieldAccessor\Constant;
// get mapper
$mapper = $container->get('bcc_auto_mapper.mapper');
// create default map and override members
$mapper->createMap('My\SourcePost', 'My\DestinationPost')
->forMember('title', new Constant('Constant title'));
// create objects
$source = new SourcePost();
$source->name = 'AutoMapper Bundle';
$destination = new DestinationPost();
// map
$mapper->map($source, $destination);
echo destination->title; // outputs 'Constant title'
You can apply a filter to a mapped member. Right now there is just a IfNull
filter that applies a default value if the field could not be mapped or is mapped on a null value:
<?php
use BCC\AutoMapperBundle\Mapper\FieldAccessor\IfNull;
// get mapper
$mapper = $container->get('bcc_auto_mapper.mapper');
// create default map and override members
$mapper->createMap('My\SourcePost', 'My\DestinationPost')
->filter('title', new IfNull('Default title'));
// create objects
$source = new SourcePost();
$source->name = 'AutoMapper Bundle';
$destination = new DestinationPost();
// map
$mapper->map($source, $destination);
echo destination->title; // outputs 'Default title'
You can define map and add them to the Mapper at the container level.
Extend the BCC\AutoMapperBundle\Mapper\AbstractMap
class:
<?php
namespace My;
use BCC\AutoMapperBundle\Mapper\AbstractMap;
class PostMap extends AbstractMap {
function __construct() {
$this->buildDefaultMap(); // generate the default map
$this->route('title', 'name'); // override the title member
}
public function getDestinationType() {
return 'My\DestinationPost';
}
public function getSourceType() {
return 'My\SourcePost';
}
}
Don't forget to declare it as a service with the bcc_auto_mapper.map
tag:
<service id="my.map" class="My\PostMap">
<tag name="bcc_auto_mapper.map" />
</service>
You can now use the mapper directly:
<?php
// get mapper
$mapper = $container->get('bcc_auto_mapper.mapper');
// create objects
$source = new SourcePost();
$source->name = 'AutoMapper Bundle';
$destination = new DestinationPost();
// map
$mapper->map($source, $destination);
echo destination->title; // outputs 'AutoMapper Bundle'
You can ignore a destination field.
<?php
// get mapper
$mapper = $container->get('bcc_auto_mapper.mapper');
// create default map
$mapper->createMap('My\SourcePost', 'My\DestinationPost')
->ignoreMember('description');
// create objects
$source = new SourcePost();
$source->description = 'Symfony2 developer';
$destination = new DestinationPost();
// map
$mapper->map($source, $destination);
var_dump(destination->description); // ignored, will be null
You can have the mapper not overwrite a field that is set on the destination.
<?php
// get mapper
$mapper = $container->get('bcc_auto_mapper.mapper');
// create default map
$mapper->createMap('My\SourcePost', 'My\DestinationPost')
->setOverwriteIfSet(false);
// create objects
$source = new SourcePost();
$source->description = 'Symfony2 developer';
$destination = new DestinationPost();
$destination->description = 'Foo bar';
// map
$mapper->map($source, $destination);
var_dump(destination->description); // will be 'Foo bar'