Aller au contenu principal

Les DTOs : arrêtez de transformer vos données en kebab

Découvrez pourquoi les DTOs sont essentiels pour des données bien rangées. Promis, pas de sauce blanche dans le code !
Catégorie

PHP

Fonctionnalités du langage, évolutions récentes et techniques avancées en PHP 8.x.

Lecture
4 min
Niveau
Intermédiaire
avr 13 2025
Partager

Vous connaissez cette sensation. Ce moment précis où vous ouvrez une méthode processData(array $data) écrite il y a six mois par un collègue (ou pire, par vous-même).

Vous regardez $data['user_info']['meta_data'][0]['valeur_reelle_final_v2'] et vous sentez le désespoir monter. De quoi est fait ce tableau ? Est-ce que cette clé existe toujours ? Pourquoi est-ce que parfois c'est une string "null" et parfois un vrai null ?

C'est ce que j'appelle le "syndrome du kebab". On empile des couches de viande et de légumes dans un pain (le tableau associatif), ça dégouline de sauce partout, c'est instable, et si vous appuyez trop fort, tout s'effondre sur votre clavier.

Il existe un remède. Il ne demande pas une ordonnance, juste un peu de rigueur : le DTO (Data Transfer Object).

Pourquoi nous infligeons-nous la douleur des tableaux ?

Les tableaux associatifs sont la drogue douce du développeur PHP. Faciles à créer, flexibles, rapides. "Je vais juste passer un petit tableau d'options", vous dites-vous.

Trois mois plus tard, ce "petit tableau" est devenu un monstre tentaculaire traversant 15 services, et plus personne n'ose toucher à une clé de peur de casser une fonctionnalité obscure en production.

Le DTO est l'antidote. C'est un contrat. C'est une promesse.

La révélation PHP 8 : le DTO readonly

Avec les versions modernes de PHP, créer un DTO n'est plus une corvée verbeuse avec 50 getters et setters. C'est élégant.

Regardez ce que j'utilise pour traiter les données de l'API RTE (Réseau de Transport d'Électricité) dans ce projet. L'API renvoie un JSON complexe, parfois incohérent. Au lieu de laisser ce chaos entrer dans mon domaine, je le stoppe net à la frontière avec un DTO blindé.

PHP
namespace App\Dto\CarbonAware;

use Symfony\Component\ObjectMapper\Attribute\Map;

/**
 * DTO for RTE éCO₂mix national real-time data.
 * Plus de devinettes. On sait exactement ce qu'on a.
 */
final readonly class RteNationalDataDto
{
    public function __construct(
        #[Map(source: 'date_heure')]
        public \DateTimeImmutable $dateHeure,

        public int $consommation,

        // On peut même mapper des objets imbriqués automatiquement !
        #[Map(target: RteProductionDto::class)]
        public RteProductionDto $production,

        #[Map(source: 'ech_physiques')]
        public int $imports,

        // Valeurs par défaut = Sécurité absolue
        public bool $isFallback = false,
        public ?string $source = 'rte',
    ) {
    }
    
    /**
     * Le DTO peut porter de la logique de PRESENTATION ou de CALCUL simple.
     * PAS de logique métier complexe (accès BDD, appels API...).
     */
    public function getCarbonIntensity(): int
    {
        return $this->tauxCo2;
    }
}

Qu'est-ce qu'on gagne immédiatemment ?

  1. Immutabilité (readonly) : Une fois créé, cet objet ne changera jamais. Vous pouvez le passer à 10 services, aucun ne pourra le modifier en douce. C'est la tranquillité d'esprit absolue.
  2. Typage fort : Si la consommation n'est pas un entier, l'application plante immédiatement à l'entrée, avec une erreur claire. Pas de "NaN" qui se propage silencieusement jusqu'à la facture du client.
  3. Intellisense : Votre IDE connait chaque propriété. Fini le Ctrl+F pour chercher les clés de tableau.

La magie noire de Symfony : Object Mapper

"Mais Pierre, c'est pénible d'hydrater ces objets ! Il faut faire des new Dto($data['truc'], $data['machin']...) partout !"

C'était vrai... Avant... Maintenant, j'utilise le composant Object Mapper.

PHP
final readonly class RteDataMapper
{
    public function __construct(
        private ObjectMapperInterface $objectMapper,
    ) {
    }

    public function map(array $data): RteNationalDataDto
    {
        // Une ligne. C'est tout.
        // L'ObjectMapper lit les attributs #[Map], convertit les types,
        // instancie les sous-DTOs et gère les erreurs.
        return $this->objectMapper->map($data, RteNationalDataDto::class);
    }
}

C'est presque indécent de simplicité. L'attribut #[Map(source: 'date_heure')] dans le DTO fait le pont entre le chaos du monde extérieur (le snake_case de l'API) et l'ordre de votre domaine (le camelCase de votre classe).

Le mot de la fin

Arrêtez de maltraiter vos données. Donnez-leur une maison. Un DTO, c'est un investissement de 2 minutes qui vous épargnera 2 heures de debugging dans six mois.

Si vos méthodes acceptent encore des array $options, posez ce clavier, respirez un grand coup, et créez votre premier DTO. Vos "futur vous" vous remercieront dans six mois quand il faudra apporter des nouvelles fonctionnalités !

Poursuivre la lecture

Sélectionné avec soin pour vous.

Symfony

Générer son SEO avec Symfony AI et Gemini

Implémentez la génération automatique de titre, description et mots-clés SEO avec Symfony AI, Gemini et Messenger dans votre administration EasyAdmin

12 min de lecture
Symfony

Attributs PHP : métadonnées natives et fin de la magie

Analyse technique de la transition Annotations -> Attributs. Pourquoi la Reflection API native surpasse le parsing de DocBlock. Exemples #[Autowire], #[MapEntity].

3 min de lecture
PHP

Design patterns en PHP : les héros de l'ombre

Découvrez l'impact réel des Design Patterns en PHP et Symfony. Ces solutions éprouvées structurent et optimisent votre code au quotidien. Simplifiez votre développement!

5 min de lecture