Le 14 octobre 2024, l'équipe core Doctrine publie simultanément ORM 3.3.0 et DBAL 4.2.0, dans la foulée directe de leur meetup annuel à Bonn. Une version discrète en apparence, mais qui règle deux dettes techniques majeures pour les équipes en pleine migration depuis ORM 2.x : les DTOs imbriqués via NEW et le retour de SELECT PARTIAL en DQL. Si tes endpoints de listing pompent encore 200 Mo de mémoire pour servir cinquante champs en JSON, lis la suite.
Bonn, un meetup qui débouche sur du code livré
Le rassemblement physique de l'équipe core à Bonn n'est pas une formalité : c'est là que les RFC en suspens depuis des mois trouvent leur verdict et que les priorités sont arbitrées. ORM 3.3.0 est le résultat direct de ces discussions. DBAL 4.2.0 l'accompagne avec des corrections de compatibilité sur les types custom (JSON, DateTimeImmutable) et des gains sur les drivers PDO. Pour les projets Symfony 7.1 déjà sur ORM 3.x, cette paire est à déployer dès que tes tests sont verts — aucun changement de schéma ni de migration SQL n'est nécessaire pour profiter des deux nouvelles fonctionnalités.
DTOs imbriqués : la syntaxe NEW s'imbrique enfin
Depuis ORM 2, l'expression NEW en DQL permet de projeter directement un DTO sans passer par l'hydratation complète des entités. La contrainte : il fallait aplatir toutes les données dans un seul constructeur, ce qui produisait des DTOs anémiques ou une prolifération de classes à un seul niveau. ORM 3.3.0 autorise l'imbrication de NEW à l'intérieur d'une expression NEW parente. Les Value Objects sont enfin des citoyens de première classe en DQL.
// src/DTO/AddressDTO.php
final class AddressDTO
{
public function __construct(
public readonly string $city,
public readonly string $country,
) {}
}
// src/DTO/AuthorDTO.php
final class AuthorDTO
{
public function __construct(
public readonly int $id,
public readonly string $name,
public readonly AddressDTO $address,
) {}
}// src/Repository/AuthorRepository.php
$dql = <<<DQL
SELECT NEW App\DTO\AuthorDTO(
a.id,
a.name,
NEW App\DTO\AddressDTO(
a.city,
a.country
)
)
FROM App\Entity\Author a
WHERE a.active = true
ORDER BY a.name ASC
DQL;
/** @var AuthorDTO[] $authors */
$authors = $this->getEntityManager()
->createQuery($dql)
->getResult();
// Zéro entité hydratée, zéro état UnitOfWork attaché.Le gain est immédiat sur les endpoints de listing : tu obtiens des objets immuables, prêts à sérialiser, sans proxy Doctrine, sans lazy-loading intempestif. Un graphe d'auteurs avec adresses qui coûtait 80 requêtes N+1 en eager-loading classique se réduit à une seule passe DQL. La mémoire suit : pas d'entité attachée à l'UnitOfWork, pas de changeset à tracker.
SELECT PARTIAL de retour : fin de la sur-hydratation involontaire
Migration Symfony en vue ?
Estimer ma migration →SELECT PARTIAL avait disparu dans les premières releases de ORM 3.x, laissant les équipes sans solution propre pour charger un sous-ensemble de colonnes sans basculer sur du SQL natif. ORM 3.3.0 restaure la fonctionnalité avec la même syntaxe qu'ORM 2 : les champs à charger listés entre accolades après l'alias.
// Chargement partiel : id, title, slug, publishedAt — les autres champs
// restent non initialisés. Accéder à $post->content lève une exception.
$dql = 'SELECT PARTIAL p.{id, title, slug, publishedAt}
FROM App\Entity\Post p
WHERE p.status = :status
ORDER BY p.publishedAt DESC';
$posts = $entityManager
->createQuery($dql)
->setParameter('status', 'published')
->setMaxResults(20)
->getResult();
// $posts : Post[] partiellement hydratés, toujours dans l'UnitOfWork.Attention : contrairement aux DTOs, les entités partiellement hydratées restent dans le cycle de vie Doctrine. Appeler un getter sur un champ non chargé lève une UninitializedFieldException — c'est le comportement attendu, documente-le dans tes code reviews. PARTIAL reprend tout son sens pour les batches d'update où tu as besoin de l'UnitOfWork (changeset tracking, events, listeners) sans charger des colonnes TEXT ou BLOB volumineuses. Pour de la lecture pure vers une API, l'approche DTO imbriqué reste supérieure.
Mise à jour : trois commandes, zéro migration SQL
SELECT PARTIALpeut être réintégré tel quel depuis tes DQL ORM 2 : la syntaxe est rétrocompatible.- Les DTOs imbriqués remplacent les jointures complexes qui finissaient en
array_mapcôté PHP. - DBAL 4.2.0 corrige les types
JSONetDateTimeImmutablesur certains drivers — vérifie tes fixtures de persistance. - Aucun changement de schéma SQL, aucune migration Doctrine à générer.
composer require doctrine/orm:^3.3 doctrine/dbal:^4.2
php bin/console doctrine:schema:validate
php bin/phpunit --testsuite=integrationORM 3.3.0 n'est pas une révolution, c'est un nettoyage de dette ciblé — exactement ce dont ont besoin les équipes bloquées à mi-chemin entre ORM 2 et le modèle objet valeur qu'elles visaient. Si votre migration stagne, que vos endpoints de listing sur-hydratent encore ou que vos DTOs actuels ressemblent à des tableaux déguisés, un Bear Upgrade permet de dresser le diagnostic, d'identifier les DQL à réécrire en priorité et de piloter la bascule ORM 3.x sans régressions en production.
Migration Symfony en vue ?
Bear Upgrade migre votre codebase 3-5× moins cher qu'une ESN.