[ERP-72] Paginer toutes les collections API + regle pagination obligatoire (#28)
Auto Tag Develop / tag (push) Successful in 7s

## Contexte

Ticket Lesstime : [#72](https://lesstime.malio.fr/project/6/task/491) (id 491) — ticket transversal, pas de spec dediee : la description du ticket fait foi.

## Implementation

- **Defaut global de pagination** dans `config/packages/api_platform.yaml` : `items_per_page=10`, `maximum_items_per_page=50`, `client_items_per_page=true`, **`client_enabled=true`** (echappatoire `?pagination=false` pour alimenter les `<select>` cote front).
- **`CategoryProvider` refondu** : retourne maintenant un `ApiPlatform\Doctrine\Orm\Paginator(Doctrine\ORM\Tools\Pagination\Paginator(...))` au lieu d'un array brut. Supporte `?pagination=false`.
- **`AuditLogResource`** : override `paginationItemsPerPage=30 / max=50 / clientItemsPerPage=true` supprime, herite du global (10/50). `AuditLogProvider` (`DbalPaginator`) inchange.
- **Autres ressources** (`Category`, `CategoryType`, `User`, `Role`, `Permission`, `Site`) : aucun changement de code, heritent automatiquement.
- **Regle « pagination obligatoire »** documentee : `CLAUDE.md` (regle ABSOLUE n°13 + section « A NE PAS faire ») + `.claude/rules/backend.md` (nouvelle section dediee avec standard, override, selects, providers customs, garde-fou).
- **Garde-fou CI** : `tests/Architecture/CollectionsArePaginatedTest` echoue si une `GetCollection` desactive la pagination sans whitelist `EXCLUDED`.

## Adaptation collaterale (non prevue au plan initial)

7 appels `GET /api/<collection>` dans les tests existants (`CategoryListTest`, `PermissionApiTest`, `RoleApiTest`) ont recu `?pagination=false` parce qu'ils asseyaient sur le contenu complet de l'array. Sans cette adaptation, le commit Task 1 cassait `PermissionApiTest::testCollectionFilterByOrphanFalse`.

## Criteres d'acceptation

- [x] Toutes les collections API existantes paginees (plus aucun retour complet)
- [x] `itemsPerPage` par defaut (10) + max borne (50)
- [x] Tri / filtres / recherche fonctionnent combines a la pagination
- [x] `hydra:totalItems` (cle `totalItems` en JSON-LD API Platform 4) expose pour le front
- [x] Regle documentee (`CLAUDE.md` + `.claude/rules/backend.md`)

## Tests

- `docker exec -t php-starseed-fpm php -d memory_limit=512M vendor/bin/phpunit` → **Tests: 320, 0 failures** (etait 312 avant ce ticket → +8 nouveaux : 5 `CategoryPaginationTest` + 2 `AuditLogPaginationRegressionTest` + 1 `CollectionsArePaginatedTest`)
- `make php-cs-fixer-allow-risky` → 0 fix
- Verifications HTTP manuelles : voir cahier de test dans le ticket Lesstime #72

## Note d'incident

Le tout premier commit (`9060f5d`, pose du standard YAML) a ete cree avec `--no-verify` par un subagent qui n'a pas respecte la consigne explicite « jamais de bypass de hook ». La cause sous-jacente du hook failure etait un drift BDD locale sur `ColumnsHaveSqlCommentTest`, resolu ensuite via `make db-reset`. Les 6 commits suivants ont passe le hook normalement. Le contenu de `9060f5d` est correct (15 lignes YAML ajoutees) — a re-verifier en review.

## Reviewer suggere

A definir (Tristan etant l'auteur).

## Suite

Debloque le volet front **ERP-73** (pagination `MalioDataTable` + composable reutilisable + cablage `?pagination=false` sur les composables de select Role/Permission/Site/CategoryType).

Reviewed-on: #28
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
This commit was merged in pull request #28.
This commit is contained in:
2026-05-29 14:15:41 +00:00
committed by Autin
parent 1d91b4dea9
commit 3e46394be1
12 changed files with 545 additions and 19 deletions
@@ -29,18 +29,16 @@ use App\Module\Core\Infrastructure\ApiPlatform\State\Provider\AuditLogProvider;
* ?performed_at[after]=2026-04-01T00:00:00Z
* ?performed_at[before]=2026-04-30T23:59:59Z
*
* La pagination est assuree par le provider via DbalPaginator (implementant
* ApiPlatform\State\Pagination\PaginatorInterface), ce qui genere
* automatiquement hydra:view — aucune construction manuelle.
* La pagination herite du standard global (10 items / page, max 50, cf.
* `config/packages/api_platform.yaml`). Elle est materialisee par le
* DbalPaginator du provider qui implemente PaginatorInterface — API Platform
* genere automatiquement hydra:view sans construction manuelle.
*/
#[ApiResource(
shortName: 'AuditLog',
operations: [
new GetCollection(
uriTemplate: '/audit-logs',
paginationItemsPerPage: 30,
paginationClientItemsPerPage: true,
paginationMaximumItemsPerPage: 50,
security: "is_granted('core.audit_log.view')",
provider: AuditLogProvider::class,
),
@@ -68,6 +68,13 @@ final readonly class AuditLogProvider implements ProviderInterface
*/
private function provideCollection(Operation $operation, array $context): DbalPaginator
{
// Contrairement aux ressources ORM (cf. CategoryProvider), ce provider
// ne gere PAS l'echappatoire `?pagination=false` : la pagination y est
// toujours forcee. `audit_log` est une table append-only a croissance
// infinie — la dumper entierement saturerait memoire/reseau et n'a aucun
// usage front (pas de <select> alimente par l'audit). Le flag global
// `pagination_client_enabled: true` reste donc volontairement inerte ici.
//
// `page` brut peut etre <= 0 (parametre client) → OFFSET negatif → 500 PG
// (`SQLSTATE[22023] OFFSET must not be negative`). API Platform clampe
// `itemsPerPage` au max de la resource mais pas `page` ; on impose un