docs(arch) : regle ABSOLUE n°13 'pagination obligatoire' + standard backend
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Successful in 1m20s
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Successful in 1m2s

This commit is contained in:
2026-05-29 15:14:16 +02:00
parent f45bdc4e5a
commit 45f7fa17c3
2 changed files with 60 additions and 0 deletions
+58
View File
@@ -13,6 +13,64 @@
- Le login `/login_check` est **hors** prefix `/api` (nginx reecrit `REQUEST_URI` vers `/login_check`)
- **Exception** : si tu dois creer un controller custom sous `/api/`, mettre `priority: 1` sur `#[Route]` pour eviter le conflit avec API Platform `{id}`
## Pagination (obligatoire)
**Regle** : toute collection API DOIT etre paginee. Aucun retour de collection complete cote serveur.
### Standard global
Pose dans `config/packages/api_platform.yaml` (section `defaults:`) et heritee par toutes les ressources :
| Cle | Valeur | Effet |
|---|---|---|
| `pagination_enabled` | `true` | Pagination Hydra active par defaut. |
| `pagination_items_per_page` | `10` | Taille de page par defaut, aligne sur l'UI `MalioDataTable`. |
| `pagination_maximum_items_per_page` | `50` | Borne dure : `?itemsPerPage=999` → ramene a 50. Anti deep-fetch. |
| `pagination_client_items_per_page` | `true` | Le client peut envoyer `?itemsPerPage=25` (bornee par le max). |
| `pagination_client_enabled` | `true` | Le client peut envoyer `?pagination=false` pour TOUT recuperer (echappatoire selects). |
### Override par ressource (rare)
Si une ressource a besoin d'un autre defaut (ex: payload lourd), utiliser les attributs sur l'operation. **JAMAIS `paginationEnabled: false`** sans whitelist explicite dans `tests/Architecture/CollectionsArePaginatedTest::EXCLUDED`.
```php
new GetCollection(
paginationItemsPerPage: 5, // override taille par defaut
paginationMaximumItemsPerPage: 20, // override borne max
)
```
### Selects et autocompletions
Pour alimenter un `<select>` ou un drawer RBAC (Role, Permission, Site, CategoryType), le front passe :
```ts
useApi().get('/api/roles?pagination=false')
```
Le serveur retourne toute la collection, sans `view`. C'est l'echappatoire prevue par `pagination_client_enabled: true`. Sur les ressources a forte volumetrie, preferer une saisie assistee (recherche serveur via `?q=`) — a planifier dans un ticket dedie.
Les tests fonctionnels qui exercent ce comportement doivent egalement passer `?pagination=false` (cf. `CategoryListTest`, `PermissionApiTest`).
### Providers customs et pagination
Un provider custom qui retourne un `array` brut sur une `CollectionOperationInterface` **court-circuite la pagination Hydra** (pas de `totalItems`, pas de `view`). Patterns supportes :
- **ORM** : injecter `ApiPlatform\State\Pagination\Pagination`, wrap un `Doctrine\ORM\Tools\Pagination\Paginator` dans `ApiPlatform\Doctrine\Orm\Paginator`. Exemple : `CategoryProvider`.
- **DBAL** : implementer un paginator local conforme a `PaginatorInterface`. Exemple : `DbalPaginator` (Core) + `AuditLogProvider`.
Gerer l'echappatoire `?pagination=false` :
```php
if (!$this->pagination->isEnabled($operation, $context)) {
return $qb->getQuery()->getResult(); // tout retourner
}
```
### Garde-fou architecture
`tests/Architecture/CollectionsArePaginatedTest` scanne reflexivement toutes les classes `#[ApiResource]` sous `src/` et echoue si une `GetCollection` pose `paginationEnabled: false` hors whitelist `EXCLUDED`. Ajouter une entree a la whitelist requiert une justification courte + un ticket Lesstime ouvert.
## Repositories
- Interface : `*RepositoryInterface` dans `Domain/Repository/`
+2
View File
@@ -25,6 +25,7 @@ Doc humaine : @README.md — Spec audit : @doc/audit-log.md
10. **Jamais mentionner Claude, Anthropic ou une IA** dans un commit (message, titre, body, footer, trailer) ou une PR (titre, description). Pas de `Co-Authored-By: Claude`, pas de `Generated with Claude Code`, pas de `🤖`, pas d'emoji robot, rien. Les commits sont signes par l'utilisateur uniquement.
11. **Migrations d'initialisation au namespace racine** `DoctrineMigrations` dans `migrations/` (setup user, RBAC, seed de base). Les migrations modulaires (`src/Module/*/Infrastructure/Doctrine/Migrations/`) sont reservees aux evolutions post-schema (ajout de colonnes, index) — cf. @.claude/rules/architecture.md pour la raison.
12. **Toujours documenter chaque colonne BDD via `COMMENT ON COLUMN`** dans la migration qui la cree ou la modifie. Description en francais, courte (≤ 200 caracteres), explique la semantique metier + contraintes implicites (unicite partielle, FK importante, lien RG). Garde-fou : `tests/Architecture/ColumnsHaveSqlCommentTest` echoue si une colonne `public` n'a pas de description (`col_description IS NULL`). Details et exemples : @.claude/rules/backend.md § Migrations Doctrine.
13. **Toujours paginer toute collection exposee par l'API.** Aucun retour de collection complete (pas de provider qui retourne un array brut). Standard pose dans `config/packages/api_platform.yaml` : 10 items par defaut, max 50, le client peut basculer entre 10/25/50 et peut envoyer `?pagination=false` pour alimenter un select. Garde-fou : `tests/Architecture/CollectionsArePaginatedTest` echoue si une `GetCollection` desactive la pagination sans whitelist. Details et exemples : @.claude/rules/backend.md § Pagination.
## Conventions
@.claude/rules/architecture.md
@@ -53,6 +54,7 @@ Editer uniquement `config/modules.php` (commenter la ligne). Cascade automatique
## A NE PAS faire
- Pas de controller Symfony custom sous `/api/` sans `priority: 1` sur `#[Route]` (conflit API Platform `{id}`).
- Pas de provider API Platform qui retourne un array brut sur une `GetCollection` — court-circuite la pagination Hydra (`totalItems` / `view` absents). Utiliser `ApiPlatform\Doctrine\Orm\Paginator` (ORM) ou un paginator implementant `PaginatorInterface` (DBAL — cf. `DbalPaginator`).
- Pas de `getClientMimeType()` pour valider un upload — utiliser `$file->getMimeType()` (serveur).
- Pas de hardcode de la sidebar cote front, pas de `modules-loader.ts` ni `.module.ts`.
- Pas d'edition manuelle de `extends` dans `frontend/nuxt.config.ts` — les layers sont scannes automatiquement.