Accéder au contenu principal

4min.

AutoMapper 10.0: High-performance mapping ready for the future

Cet article est aussi disponible en 🇫🇷 Français : AutoMapper 10.0 : Le mapping haute performance prêt pour le futur.

This is a major milestone for the library: AutoMapper has reached version 10.0.

While our promise remains unchanged — transforming your data from one format to another as fast as possible — this version marks a technological breakthrough. We took advantage of this major release to modernize the core engine and align with the latest Symfony standards: and much more!

Section intitulée under-the-hood-the-arrival-of-symfony-type-infoUnder the hood: The arrival of symfony/type-info

This is the most impactful invisible change. I have been working on Jane and AutoMapper for a long time, and I started from a simple observation: a tool capable of retrieving metadata in an advanced way was missing. At the time, Symfony’s PropertyInfo component had limited support for unions and handled neither intersections nor generics.

I initially implemented the PHPStan extractor in PropertyInfo to try to fill these gaps, but I quickly realized that the system remained limited by the component’s very design. It was following discussions with Mathias Arlaud that we decided to team up to create and release the TypeInfo component.

That is why TypeInfo (introduced in Symfony 7.1) is now the new standard for obtaining even more detailed type definitions. With v10, AutoMapper switches to this component. The more precise the type definition, the more reliable and optimal the mapping process can be. This is an essential step to guarantee a much more faithful extraction of your data.

Section intitulée the-new-feature-forced-typingThe new feature: Forced Typing

Sometimes, automatic inference isn’t enough, or you want to transform data into a specific format that differs from the source property’s PHP type. v10 allows you to force the target type directly via the #[MapTo] attribute.

Example:

Here, we explicitly ask the mapper to transform the $number property into an array of integers in the target, even if the source is a string.

class Entity {
    #[MapTo(target: 'array', targetPropertyType: 'int')]
    public string $number;
}

Section intitulée interoperability-symfony-objectmapper-supportInteroperability: Symfony ObjectMapper Support

Much like AutoMapper’s native interface, the ObjectMapperInterface allows transforming a source object into a target object via a single method. The strength of this signature lies in the $target parameter, which accepts either a class name (to create a new instance) or an existing object (to hydrate or update it).

interface ObjectMapperInterface
{
    public function map(object $source, object|string|null $target = null): object;
}

This is where AutoMapper makes a difference: whereas Symfony’s native implementation relies mainly on the Reflection API (which can be costly), AutoMapper generates optimized PHP code. By activating this support, you replace the default mechanism with a solution tailored for performance, which is much faster at runtime. To activate this feature and replace Symfony’s default ObjectMapper with the AutoMapper one, a single line in the bundle configuration is enough:

automapper:
    object_mapper: true

To make this possible and efficient, we had to implement a highly anticipated feature: Nesting. This allows reading or writing deeply nested values in your objects, drastically simplifying the transition from “flat” DTOs to rich entities.

Example: Here we map a flat UserDto to a User containing an Address object. Thanks to the address.street notation, AutoMapper knows it must hydrate the sub-object.

class UserDto {
    #[MapTo(property: 'address.street')]
    public string $streetAddress;

    #[MapTo(property: 'address.city')]
    public string $cityAddress;

    public string $name;
}

class User {
    public Address $address;
    public string $name;

    public function __construct()
    {
        $this->address = new Address();
    }
}

class Address {
    public string $street;
    public string $city;
}

// In use, the Address sub-object is automatically populated
$mapper->map(new UserDto(
    streetAddress: '123 Main St',
    cityAddress: 'Springfield',
    name: 'John Doe'
), User::class);

Section intitulée polymorphism-rethinking-discriminator-managementPolymorphism: Rethinking discriminator management

Until now, handling polymorphism relied on a dependency on the Symfony Serializer. It was limited by the necessity to specify a discrimination property. v10 improves this by allowing you to define the discrimination logic directly at the class level via the #[Mapper] attribute, enabling object mapping without having to define a property to discriminate entities. This allows for seamless mapping from DTO hierarchies to Entity hierarchies.

Example: Here you define how to map the children of the abstract Pet class based on the incoming DTO type.

#[Mapper(discriminator: new Discriminator(
    mapping: [
        DogDto::class => Dog::class,
        CatDto::class => Cat::class,
    ]
))]
abstract class Pet {
    /** @var string */
    public $name;

    /** @var PetOwner */
    public $owner;
}

Section intitulée a-new-profiler-to-see-clearlyA new Profiler to see clearly

Mapping is often a “black box”. When data comes out malformed, understanding why can be complex.

We have revamped the integration into the Symfony Profiler. Gone are the days when you didn’t know if AutoMapper had actually worked. Now, the dedicated panel allows you to:

  • List all mappings performed during the request.
  • See the Source class and the Target class.
  • Inspect the context passed to the mapper.

It is an indispensable tool for debugging your complex transformations without having to place breakpoints everywhere.

Profiler

Section intitulée final-wordFinal Word

Version 10 is the culmination of a lot of work to modernize and sustain the library. A huge thank you to all the contributors who participated in this release, with a special mention to Joel Wurtz for his colossal investment in this major version.

To go further, everything happens here:

Make sure your environment is running PHP 8.4 or higher and run:

composer require jolicode/automapper ^10.0

Commentaires et discussions

Ces clients ont profité de notre expertise