uniquement les archives (is_archived = true) ; * - sinon $includeArchived = true -> actifs + archives (echappatoire) ; * - sinon (defaut) -> uniquement les actifs (is_archived = false). * $archivedOnly a la priorite sur $includeArchived. * - Tri par defaut : companyName ASC (RG-3.16). * - $search : recherche fuzzy insensible a la casse sur companyName + les * contacts lies (firstName / lastName / email) via sous-requete. * Metacaracteres LIKE echappes. Ignore si null/vide. * - $categoryCodes : restreint aux prestataires possedant au moins une * categorie dont le code est dans la liste (OR). Liste vide = pas de filtre. * - $siteIds : restreint aux prestataires rattaches a l'un des sites donnes * (OR — RG-3.03, relation DIRECTE provider.sites). Liste vide = pas de filtre. * * Filtrage centralise ICI (et non dans le provider/controller) pour que la * liste paginee et l'export partagent strictement la meme logique de selection * (miroir M2). * * Contrat = SELECTION uniquement (filtres + tri). Aucun fetch-join to-many : * l'hydratation des collections affichees est deleguee a * {@see self::hydrateListCollections()} pour ne pas imposer le cout d'un * produit cartesien aux chemins non pagines (§ 2.12, cf. M1/ERP-100, M2). * * NB : le cloisonnement par site pilote par l'utilisateur (RG-3.17, § 2.13) est * applique en AMONT par le ProviderProvider (ERP-134), pas par ce QueryBuilder * (qui ne connait pas l'user courant). * * @param list $categoryCodes * @param list $siteIds */ public function createListQueryBuilder( bool $includeArchived = false, ?string $search = null, array $categoryCodes = [], array $siteIds = [], bool $archivedOnly = false, ): QueryBuilder; /** * Hydrate en lot les collections affichees par le repertoire (categories puis * sites — relation DIRECTE provider.sites, RG-3.03) sur un jeu de prestataires * 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 * (anti N+1, § 2.12). * * Charge les categories et les sites en DEUX requetes distinctes (et non un * double fetch-join) pour ne pas multiplier categories x sites en un seul * produit cartesien. * * @param list $providers */ public function hydrateListCollections(array $providers): void; /** * Hydrate en lot la collection `contacts` sur un jeu de prestataires DEJA * charges (memes instances via l'identity map). Reservee aux chemins qui ont * besoin du contact principal (export) : la LISTE paginee n'embarque pas les * contacts (§ 2.12), d'ou une methode dediee plutot qu'une passe supplementaire * dans {@see self::hydrateListCollections()}. * * @param list $providers */ public function hydrateContacts(array $providers): void; }