AutoMapper 9 is out!
We are pleased to announce the release of AutoMapper 9.0 which brings a completely new experience creating mappers between objects 🎉
Let’s be honest, the first version of this library was merely a proof of concept. Despite being used in production on some of our projects, it was difficult to use, understand and configure for newcomers.
This new version is a rewriting of a lot of internals, and brings a lot of new features and improvements. We hope you will enjoy it as much as we do 😍
Section intitulée what-is-the-automapperWhat is the AutoMapper?
AutoMapper is a PHP object-object mapper. Object-object mapping works by transforming an input object (or array) of one type into an output object (or array) of a different type. What makes AutoMapper interesting is that it provides some conventions to take the dirty work out of figuring out how to map type A to type B. As long as type B follows AutoMapper’s established convention, almost zero configuration is needed to map two types.
When a transformation from A to B is done, AutoMapper will generate a Mapper class that will make the transformation. It looks a lot like Symfony’s normalizer and is optimized as much as possible.
Section intitulée what-s-newWhat’s new?
Section intitulée new-documentationNew Documentation
First of all we have rewritten the documentation, it is now more complete, and we hope, more understandable. It is still not perfect, specially on the examples, if you have some use cases you would like to see explained, please open an issue on the GitHub repository.
Section intitulée improved-usage-and-dxImproved Usage and DX
When you need to map an object to another, you can use the map()
method of AutoMapper
class:
$mapper = AutoMapper::create();
$result = $mapper->map($entity, EntityDTO::class);
Here, $result
will be an EntityDTO
instance. Moreover, thanks to built-in PHPDoc, static analyzer like PHPStan will interpret the value correctly.
Most of the time you won’t need more than this configuration. But in case you need it, you can now control how the mapping is done by using attributes. There are 4 attributes available:
MapTo
andMapFrom
to specify the source and target properties and how to map them;Mapper
to register a mapper and/or configure how the generation is done for it;MapProvider
to register a provider for a mapper : a different way to create the target object (i.e. fetch it from a database).
All those attributes have a lot of parameters to configure how the mapping is done, you can see the full documentation for more information.
Let’s see a complete example, where we want to do two things:
- Allow to map from an
Entity
class to anEntityDTO
class; - Allow to map from an
Entity
class to an array.
Here is our Entity
class:
class Entity
{
public ?string $title;
public User $author;
}
Here is our EntityDTO
class:
class EntityDTO
{
public string $title;
public string $authorFirstName;
public string $authorLastName;
public function __construct()
{
$this->title = 'Default title';
}
}
Section intitulée registering-a-mapperRegistering a Mapper
First we want to register mappers for both use cases, it is not mandatory but it will allow us to generate this mapper if we are using the Symfony Bundle with cache warmup, it avoids creating it on the fly which may not be possible on readonly filesystem.
use AutoMapper\Attribute as Mapper;
#[Mapper\Mapper(target: [EntityDto::class, 'array'])]
class Entity
{
...
Maybe you don’t want to use the constructor of the target object when mapping to the EntityDTO
class, you can configure that with the constructorStrategy
parameter:
use AutoMapper\Attribute as Mapper;
#[Mapper\Mapper(target: [EntityDto::class, 'array'], constructorStrategy: ConstructorStrategy::NEVER)]
class Entity
{
...
Section intitulée mapping-propertiesMapping Properties
Now we want to map the properties of the Entity
class to the EntityDTO
class, as you can see some of them don’t have the same name.
We want to flatten the author property of the Entity
class to the authorFirstName and authorLastName properties of the EntityDTO
class, this can be done by using the MapTo
attribute:
use AutoMapper\Attribute as Mapper;
#[Mapper\Mapper(target: [EntityDto::class, 'array'], constructorStrategy: ConstructorStrategy::NEVER)]
class Entity
{
...
#[Mapper\MapTo(target: EntityDto::class, property: 'firstName', transformer: 'source.author.firstName')]
#[Mapper\MapTo(target: EntityDto::class, property: 'lastName', transformer: 'source.author.lastName')]
public User $author;
...
It is also possible to do that in the EntityDTO
class with the MapFrom
attribute, which can be useful if you don’t have control of the source class (i.e. using a class from a third party library):
class EntityDTO
{
...
#[Mapper\MapFrom(source: Entity::class, transformer: 'source.author.firstName')]
public string $authorFirstName;
#[Mapper\MapFrom(source: Entity::class, transformer: 'source.author.lastName')]
public string $authorLastName;
...
}
We use the transformer
parameter to specify how the mapping is done, in this example it leverages the expression language, with the variable source being an Entity
instance in this case.
We may have used a callback or a custom transformer but in most cases the expression language can handle your logic.
Section intitulée virtual-propertiesVirtual Properties
Now we want to map this entity to an array and add an url
property that is not present in the Entity
class, we can do that with the MapTo
attribute defined on this class:
use AutoMapper\Attribute as Mapper;
#[Mapper\Mapper(target: [EntityDto::class, 'array'], constructorStrategy: ConstructorStrategy::NEVER)]
#[Mapper\MapTo(target: 'array', property: 'url', transformer: UrlTransformer::class)]
class Entity
{
...
We also need to create the UrlTransformer
class:
class UrlTransformer implements PropertyTransformerInterface
{
public function __construct(private UrlGenerator $urlGenerator)
{
}
public function transform(mixed $value, object|array $source, array $context): mixed
{
return $this->urlGenerator->generate('entity_show', ['title' => $source->title]);
}
}
Please note that the UrlTransformer
class is a service, AutoMapper will not create it for you, you need to register it either when creating the AutoMapper
instance or by using the Symfony Bundle.
That’s it, you can now map an Entity
object to an EntityDTO
object or an array with your custom logic.
Again, this shouldn’t be something you need all the time and only in very specific cases. There is a lot more you can do with AutoMapper 9 to customize your logic, please check the documentation for more information.
Section intitulée new-built-in-symfony-bundleNew Built-in Symfony Bundle
The Symfony Bundle is now integrated in the main package, we believe this will simplify the installation and configuration and avoid version mismatch between the bundle and the main package.
Section intitulée new-debugging-and-profilingNew Debugging and Profiling
We have also added tools to help you debug and profile your mappers when using the Symfony Bundle, you can now debug the generated code with the debug:mapper
command or with a new panel in the Symfony Profiler.
Section intitulée new-integration-with-symfony-s-serializer-and-api-platformNew Integration with Symfony’s Serializer and API Platform
From the start this library was also a way to improve performance of serialization in Symfony applications, and we have extended this by integrating it also with API Platform.
API Platform support is still in its early stage, but we believe it will be a great addition to the library.
However, you may not want to replace all your existing serializers at first, so we also make sure this version delivers a way to migrate progressively to AutoMapper without breaking your existing code. Look at our migration guide for more information on how to achieve this.
Section intitulée what-s-nextWhat’s next?
Further versions will focus on stability and improving third party integrations, we also want to provide more ways to customize the mappers.
If you have any idea or feature request, please open an issue or create a pull request on the GitHub repository
We hope you will enjoy this new version, and we are looking forward to seeing what you will build with it.
Happy mapping!
Commentaires et discussions
Ces clients ont profité de notre expertise
Studapart a fait appel à JoliCode pour surmonter plusieurs défis techniques que leur équipe interne ne pouvait gérer, faute de temps ou d’expertise. Nous avons travaillé ensemble pour améliorer leur processus d’onboarding des développeurs, résoudre des problématiques complexes de gestion des droits, et configurer correctement leurs certificats HTTP/HTTPS…
Groupama Épargne Salariale digitalise son expérience client en leur permettant d’effectuer leurs versements d’épargne salariale en ligne. L’application offre aux entreprises une interface web claire et dynamique, composé d’un tunnel de versement complet : import des salariés via fichier Excel, rappel des contrats souscrits et des plans disponibles, …
Dans le cadre d’une refonte complète de son architecture Web, Expertissim a sollicité l’expertise de JoliCode afin de tenir les délais et le niveau de qualité attendus. Le domaine métier d’Expertissim n’est pas trivial : les spécificités du marché de l’art apportent une logique métier bien particulière et un processus complexe. La plateforme propose…