Accéder au contenu principal

4min.

AutoMapper 10.0 : Le mapping haute performance prêt pour le futur

This blog post is also available in 🇬🇧 English: AutoMapper 10.0: High-performance mapping ready for the future.

C’est une étape majeure pour la librairie : AutoMapper passe en version 10.0.

Si notre promesse reste inchangée — transformer vos données d’un format à un autre le plus vite possible — cette version marque une rupture technologique. Nous avons profité de cette version majeure pour moderniser le cœur du réacteur et s’aligner sur les derniers standards de Symfony et bien plus encore !

Section intitulée sous-le-capot-l-arrivee-de-symfony-type-infoSous le capot : L’arrivée de symfony/type-info

C’est le changement invisible le plus impactant. Je travaille sur Jane et AutoMapper depuis longtemps, et je suis parti d’un constat simple : il manquait un outil capable de récupérer les métadonnées de façon avancée. À l’époque, le composant PropertyInfo de Symfony avait un support limité des unions et ne gérait ni les intersections, ni les génériques.

J’avais initialement mis en place l’extracteur PHPStan dans PropertyInfo pour tenter de combler ces manques, mais je me suis vite rendu compte que le système restait limité par la conception même du composant. C’est suite à des discussions avec Mathias Arlaud que nous avons décidé de nous mettre ensemble pour créer et sortir le composant TypeInfo.

C’est pourquoi TypeInfo (introduit dans Symfony 7.1) est aujourd’hui le nouveau standard pour obtenir des définitions de types encore plus détaillées. Avec la v10, AutoMapper bascule sur ce composant. Plus la définition des types est précise, plus le processus de mapping peut être effectué de manière fiable et optimale. C’est une étape essentielle pour garantir une extraction beaucoup plus fidèle de vos données.

Section intitulée la-nouveaute-le-typage-forceLa nouveauté : Le typage forcé

Parfois, l’inférence automatique ne suffit pas ou vous souhaitez transformer une donnée vers un format précis qui diffère du type PHP de la propriété source. La v10 permet de forcer le type cible directement via l’attribut #[MapTo].

Exemple : Ici, on demande explicitement au mapper de transformer la propriété $number en un entier dans la cible, même si la source est une string.

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

Section intitulée interoperabilite-support-de-symfony-objectmapperInteropérabilité : Support de Symfony ObjectMapper

Un peu comme l’interface native d’AutoMapper, l’ObjectMapperInterface permet de transformer un objet source en un objet cible via une méthode unique. La force de cette signature réside dans le paramètre $target, qui accepte soit le nom d’une classe (pour créer une nouvelle instance), soit un objet existant (pour l’hydrater ou le mettre à jour).

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

Et c’est ici que AutoMapper fait la différence : là où l’implémentation native de Symfony s’appuie principalement sur l’API Reflection qui peut être coûteuse, AutoMapper génère du code PHP optimisé. En activant ce support, vous remplacez le mécanisme par défaut par une solution taillée pour la performance, beaucoup plus rapide à l’exécution.

Pour activer cette fonctionnalité et remplacer l’ObjectMapper par défaut de Symfony par celui de l’AutoMapper, il suffit d’une ligne dans la configuration du bundle :

automapper:
    object_mapper: true

Pour rendre cela possible et efficace, nous avons dû implémenter une fonctionnalité très attendue : le Nesting. Cela permet d’aller lire ou écrire des valeurs profondément imbriquées dans vos objets, simplifiant drastiquement le passage de DTOs « à plat » vers des entités riches.

Exemple : On mappe ici un UserDto plat vers un User qui contient un objet Address. Grâce à la notation address.street, AutoMapper sait qu’il doit hydrater le sous-objet.

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;
}

// À l'utilisation, le sous-objet Address est automatiquement peuplé
$mapper->map(new UserDto(
    streetAddress: '123 Main St',
    cityAddress: 'Springfield',
    name: 'John Doe'
), User::class);

Section intitulée polymorphisme-une-gestion-des-discriminants-repenseePolymorphisme : Une gestion des discriminants repensée

Jusqu’ici, gérer le polymorphisme reposait sur une dépendance au Serializer de Symfony. Il était limité par la nécessité de spécifier une propriété de discrimination. La v10 améliore cela en permettant de définir la logique de discrimination directement au niveau de la classe via l’attribut #[Mapper] et permet de définir un mapping d’objets sans avoir à définir une propriété pour discriminer les entités.

Exemple : Vous définissez ici comment mapper les enfants de la classe abstraite Pet en fonction du type de DTO entrant.

#[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 un-nouveau-profiler-pour-y-voir-clairUn nouveau Profiler pour y voir clair

Le mapping est souvent une « boîte noire ». Quand une donnée ressort mal formée, comprendre pourquoi peut être complexe. Nous avons revu l’intégration dans le Profiler Symfony. Fini le temps où vous ne saviez pas si AutoMapper avait réellement travaillé. Désormais, le panneau dédié vous permet de :

  • Lister tous les mappings effectués durant la requête ;
  • Voir la classe Source et la classe Cible ;
  • Inspecter le contexte passé au mapper.

C’est un outil indispensable pour debugger vos transformations complexes sans devoir placer des points d’arrêt partout.

Profiler

Section intitulée le-mot-de-la-finLe mot de la fin

Cette version 10 est l’aboutissement de beaucoup de travail pour moderniser et pérenniser la librairie. Un immense merci à tous les contributeurs qui ont participé à cette release, avec une mention spéciale à Joel Wurtz pour son investissement colossal sur cette version majeure.

Pour aller plus loin, tout se passe ici :

Assurez-vous que votre environnement tourne sous PHP 8.4 ou supérieur et lancez :

composer require jolicode/automapper ^10.0

Commentaires et discussions

Ces clients ont profité de notre expertise