From a97adb1dd9367d343f787f188011e77bb6360088 Mon Sep 17 00:00:00 2001 From: Matthieu Date: Wed, 3 Jun 2026 11:36:33 +0200 Subject: [PATCH] =?UTF-8?q?refactor(commercial)=20:=20d=C3=A9coupler=20l'h?= =?UTF-8?q?ydratation=20des=20collections=20de=20la=20s=C3=A9lection=20cli?= =?UTF-8?q?ents=20(ERP-100)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit createListQueryBuilder() redevient filtres + tri seuls (contrat de l'interface) : plus de fetch-join to-many imposé à tous les appelants. L'hydratation des collections affichées (Catégories / Site(s)) passe par la nouvelle méthode hydrateListCollections(), appelée par la liste paginée, ?pagination=false et l'export XLSX sur leur jeu déjà borné. Deux requêtes IN séparées (catégories d'un côté, adresses+sites de l'autre) remplissent les collections via l'identity map et cassent le produit cartésien catégories × adresses × sites qui explosait sur les chemins non paginés. Ajoute un garde-fou fonctionnel sur les colonnes Catégories/Sites de l'export. Découvert en review ERP-62 (#44). --- .../Repository/ClientRepositoryInterface.php | 21 ++++++++ .../State/Provider/ClientProvider.php | 19 +++++-- .../Controller/ClientExportController.php | 5 ++ .../Doctrine/DoctrineClientRepository.php | 54 +++++++++++++++---- .../Api/ClientExportControllerTest.php | 35 ++++++++++++ 5 files changed, 120 insertions(+), 14 deletions(-) diff --git a/src/Module/Commercial/Domain/Repository/ClientRepositoryInterface.php b/src/Module/Commercial/Domain/Repository/ClientRepositoryInterface.php index 2bc081b..8bdafc7 100644 --- a/src/Module/Commercial/Domain/Repository/ClientRepositoryInterface.php +++ b/src/Module/Commercial/Domain/Repository/ClientRepositoryInterface.php @@ -33,6 +33,12 @@ interface ClientRepositoryInterface * la liste paginee (ClientProvider) et l'export (ClientExportController) * partagent strictement la meme logique de selection. * + * Contrat = SELECTION uniquement (filtres + tri). Aucun fetch-join to-many : + * l'hydratation des collections affichees est une decision de l'appelant + * (cf. {@see self::hydrateListCollections()}), pour ne pas imposer le cout + * d'un produit cartesien a un consommateur qui ne filtrerait/compterait que + * (ERP-100). + * * @param list $categoryCodes * @param list $siteIds */ @@ -43,4 +49,19 @@ interface ClientRepositoryInterface array $siteIds = [], bool $archivedOnly = false, ): QueryBuilder; + + /** + * Hydrate en lot les collections affichees par le repertoire (categories, + * adresses et leurs sites) sur un jeu de clients DEJA charges, via l'identity + * map Doctrine (memes instances). A appeler apres une selection bornee (page + * courante ou jeu d'export) pour eviter le N+1 a la serialisation, sans + * imposer de fetch-join au QueryBuilder de selection (ERP-100). + * + * Charge les categories et les adresses/sites en DEUX requetes distinctes + * (et non un triple fetch-join) pour ne pas multiplier categories x adresses + * x sites en un seul produit cartesien. + * + * @param list $clients + */ + public function hydrateListCollections(array $clients): void; } diff --git a/src/Module/Commercial/Infrastructure/ApiPlatform/State/Provider/ClientProvider.php b/src/Module/Commercial/Infrastructure/ApiPlatform/State/Provider/ClientProvider.php index 7c47760..1d90cef 100644 --- a/src/Module/Commercial/Infrastructure/ApiPlatform/State/Provider/ClientProvider.php +++ b/src/Module/Commercial/Infrastructure/ApiPlatform/State/Provider/ClientProvider.php @@ -83,8 +83,13 @@ final class ClientProvider implements ProviderInterface // Echappatoire ?pagination=false : collection complete sans Paginator // (cf. convention ERP-72 — utile pour un