Skip to content

Ajouter la pagination

Ce guide explique comment ajouter la pagination ?limit= / ?offset= à un endpoint de collection à l'aide du helper PaginationQueryParser de Nene2\Http.

Prérequis

  • Un handler de collection fonctionnel (ex. ListNotesHandler).
  • Le handler retourne une enveloppe JSON avec items, limit et offset.

Étape 1 — Appeler PaginationQueryParser::parse()

Remplacez l'extraction manuelle des paramètres de requête par le parseur. Il valide les valeurs et lance ValidationException (→ 422) si elles sont hors plage.

php
use Nene2\Http\PaginationQueryParser;

public function handle(ServerRequestInterface $request): ResponseInterface
{
    $pagination = PaginationQueryParser::parse($request); // défaut : limit=20, max=100

    $output = $this->useCase->execute(
        new ListWidgetsInput($pagination->limit, $pagination->offset),
    );

    return $this->response->create([
        'items'  => /* mapper $output->items */,
        'limit'  => $output->limit,
        'offset' => $output->offset,
    ]);
}

PaginationQuery est un DTO readonly avec deux propriétés : limit: int et offset: int.

Étape 2 — Personnaliser les limites (optionnel)

Passez $defaultLimit et $maxLimit pour remplacer les valeurs par défaut (20 et 100) :

php
$pagination = PaginationQueryParser::parse($request, defaultLimit: 10, maxLimit: 50);
ParamètreDéfautSignification
$defaultLimit20Utilisé quand ?limit= est absent
$maxLimit100Valeur maximale autorisée ; retourne 422 si dépassée

Étape 3 — Gérer l'erreur 422

PaginationQueryParser::parse() lance ValidationException quand :

  • limit < 1 ou limit > $maxLimit
  • offset < 0

ErrorHandlerMiddleware mappe automatiquement ValidationException422 validation-failed. Aucune gestion d'erreur supplémentaire n'est nécessaire dans le handler.

Exemple de réponse 422 :

json
{
  "type": "https://nene2.dev/problems/validation-failed",
  "title": "Validation Failed",
  "status": 422,
  "detail": "The request body contains invalid values.",
  "errors": [
    { "field": "limit", "message": "limit must be between 1 and 100.", "code": "out_of_range" }
  ]
}

Fonctionnement

PaginationQueryParser::parse() lit getQueryParams() de la requête PSR-7, caste les valeurs en int, les valide et retourne un DTO PaginationQuery. Les valeurs non numériques sont castées en 0 (comportement PHP de (int)) puis interceptées par la vérification limit < 1.

Étape 4 — Utiliser PaginationResponse pour standardiser l'envelope

PaginationResponse est un DTO readonly qui construit l'envelope de liste standard :

php
use Nene2\Http\PaginationResponse;

return $this->response->create(
    (new PaginationResponse(
        items:  array_map(fn ($item) => ['id' => $item->id, 'name' => $item->name], $output->items),
        limit:  $output->limit,
        offset: $output->offset,
    ))->toArray(),
);

Étape 5 — Inclure le nombre total d'enregistrements (optionnel)

Passez total quand le repository supporte une requête COUNT :

php
$total = $this->repository->countAll(); // SELECT COUNT(*) AS n FROM ...

return $this->response->create(
    (new PaginationResponse(items: /* ... */, limit: $output->limit, offset: $output->offset, total: $total))->toArray(),
);

Quand total est null (défaut), la clé est omise de la réponse.

Compromis : COUNT(*) ajoute une requête par appel. Omettez total si l'overhead est inacceptable et laissez les clients détecter la dernière page avec items.length < limit.

Étape 6 — Analyser d'autres paramètres de requête avec QueryStringParser

Utilisez QueryStringParser pour les paramètres de filtrage autres que limit/offset.

php
use Nene2\Http\QueryStringParser;

$search = QueryStringParser::string($request, 'search');   // ?string
$page   = QueryStringParser::int($request, 'page');        // ?int
$active = QueryStringParser::bool($request, 'is_active');  // ?bool

// Valeurs multiples séparées par des virgules : ?tags=php,lang → ['php', 'lang']
$tags = QueryStringParser::commaSeparated($request, 'tags'); // list<string>|null

// Clé répétée à la manière de PHP : ?tags[]=php&tags[]=api → ['php', 'api']
$tags = QueryStringParser::array($request, 'tags'); // list<string>|null

commaSeparated() découpe sur les virgules, supprime les espaces et les valeurs vides, et renvoie null si le paramètre est absent ou si la liste est vide après filtrage.

array() gère les clés répétées à la manière de PHP (?key[]=v1&key[]=v2). Les implémentations PSR-7 les analysent en ['key' => ['v1', 'v2']] dans getQueryParams(). Renvoie null si la clé est absente ou si la valeur n'est pas un tableau.

Voir aussi

  • src/Example/Note/ListNotesHandler.php — implémentation de référence avec PaginationResponse
  • src/Example/Tag/ListTagsHandler.php — deuxième exemple
  • Nene2\Http\PaginationQuery — DTO readonly
  • Nene2\Http\PaginationQueryParser — la classe parseur
  • Nene2\Http\PaginationResponse — le DTO envelope de liste
  • Nene2\Http\QueryStringParser — helpers typés pour les autres paramètres de requête

Publié sous licence MIT.