19/07/2020

Faire une pagination symfony

Repository

/**
  * Récupère une liste d'article paginés et triés par date de création.
  *
  * @param int $page Le numéro de la page
  * @param int $nbMaxByPage Nombre maximum d'articles par page     
  *
  * @throws InvalidArgumentException
  * @throws NotFoundHttpException
  *
  * @return Paginator
  */
public function findAllPagedAndSorted($page, $nbMaxByPage)
{
    if (!is_numeric($page)) {
        throw new InvalidArgumentException(
            'La valeur de l\'argument $page est incorrecte (valeur : ' . $page . ').'
        );
    }

    if ($page < 1) {
        throw new NotFoundHttpException('La page demandée n\'existe pas');
    }

    if (!is_numeric($nbMaxByPage)) {
        throw new InvalidArgumentException(
            'La valeur de l\'argument $nbMaxParPage est incorrecte (valeur : ' . $nbMaxByPage . ').'
        );
    }

    $qb = $this->createQueryBuilder('article')
        ->orderBy('article.createdAt', 'DESC');

    $query = $qb->getQuery();

    $firstResult = ($page - 1) * $nbMaxByPage;
    $query->setFirstResult($firstResult)->setMaxResults($nbMaxByPage);
    $paginator = new Paginator($query);

    if ( ($paginator->count() <= $firstResult) && $page != 1) {
        throw new NotFoundHttpException('La page demandée n\'existe pas.'); // page 404, sauf pour la première page
    }

    return $paginator;
}

Controller

/**
  * Liste les articles triés par date de création pour une page.
  *
  * @Route("/{page}", requirements={"page" = "\d+"}, name="article_index", methods={"GET"})
  *
  * @param int $page Le numéro de la page (par defaut 1)
  *
  */
public function index(int $page = 1, ArticleRepository $articleRepository): Response
{
    $nbArticleByPage = $this->getParameter('NB_ARTICLE_BY_PAGE');

    $articles = $articleRepository->findAllPagedAndSorted($page, $nbArticleByPage);

    $pagination = array(
        'page' => $page,
        'nbPages' => ceil(count($articles) / $nbArticleByPage),
        'nomRoute' => 'article_index',
        'paramsRoute' => array()
    );

    return $this->render('article/index.html.twig', [
        'articles' => $articles,
        'pagination' => $pagination
    ]);
}

NB_ARTICLE_BY_PAGE est une constante que j’ai mis dans la config (.env)

/.env

NB_ARTICLE_BY_PAGE=20

/config/services.yaml

parameters:
    NB_ARTICLE_BY_PAGE: '%env(resolve:NB_ARTICLE_BY_PAGE)%'

Vue (selector)

Dans la vue symfony voici le code à rajouter pour avoir un petit menu pour la navigation.

{% if pagination.nbPages > 0 %}

    <nav aria-label="Page navigation">
        <ul class="pagination justify-content-center">
            {% if pagination.page > 1 %}
                <li class="page-item"><a class="page-link" href="{{ path(pagination.nomRoute, pagination.paramsRoute|merge({'page': 1})) }}">Debut</a></li>
                <li class="page-item"><a class="page-link" href="{{ path(pagination.nomRoute,
                    pagination.paramsRoute|merge({'page': pagination.page-1})) }}">Précédent</a></li>
            {% endif %}
            {% for p in range(max(pagination.page-4, 1), min(pagination.page+4, pagination.nbPages)) %}
                <li class="page-item {% if p == pagination.page %}active{% endif %}"><a class="page-link" href="{{ path(pagination.nomRoute, pagination.paramsRoute|merge({'page': p})) }}">{{p}}</a></li>
            {% endfor %}
            {% if pagination.page < pagination.nbPages %}
                <li class="page-item"><a class="page-link" href="{{ path(pagination.nomRoute,
                    pagination.paramsRoute|merge({'page': pagination.page+1})) }}">Suivant</a></li>
                <li class="page-item"><a class="page-link" href="{{ path(pagination.nomRoute,
                    pagination.paramsRoute|merge({'page': pagination.nbPages})) }}">Fin</a></li>
            {% endif %}
        </ul>
    </nav>

{% endif %}