Pagination avec Doctrine : le Paginator dans un repository Symfony

La pagination avec Doctrine dans Symfony ne demande à la base que les lignes d'une page, avec LIMIT et OFFSET, puis compte le total pour situer la page.

public function findPage(int $page, int $parPage): Paginator
{
    $query = $this->createQueryBuilder('a')
        ->join('a.plat', 'p')
        ->addSelect('p')
        ->orderBy('a.creeLe', 'DESC')
        ->addOrderBy('a.id', 'DESC')
        ->setFirstResult(($page - 1) * $parPage)
        ->setMaxResults($parPage)
        ->getQuery();

    return new Paginator($query, fetchJoinCollection: false);
}

Charger cinquante lignes pour en afficher dix fonctionne, puis s'effondre à cinquante mille. setFirstResult() et setMaxResults() traduisent la page en OFFSET et LIMIT ; le Paginator de Doctrine se parcourt comme la page, et count() lance une requête de comptage sur l'ensemble, d'où le nombre de pages. Le second tri, sur id, rend l'ordre stable quand deux lignes ont la même date : sans lui, une ligne peut sauter d'une page à l'autre. Côté contrôleur, #[MapQueryParameter] avec min_range écarte ?page=0 ou ?page=abc par une 404. Le piège : un décalage écrit $page * $parPage au lieu de ($page - 1) * $parPage. La première page saute les dix lignes les plus récentes, et rien ne casse.

Les exercices font découper le livre d'or de la taverne en pages de dix avis, en trois requêtes au plus, avec une 404 pour les pages inexistantes, puis retrouver pourquoi des voyageurs ne voient pas l'avis qu'ils viennent d'écrire.

2 exercices font travailler cette notion, en écrivant du code dans le navigateur.

Dans les parcours

Notions voisines

#[MapQueryParameter]DébogageMigrationsManyToManyRequêtes N+1