Paginacja w Symfony – Knp Paginator Bundle

Bardzo często pierwsza aplikacją, na której szlifujemy swoje umiejętności jest blog. Nawet samo Symfony dało możliwość jedną komendą postawienia bloga, który przedstawia rekomendowaną strukturę. Teraz nastały czasy, że wszyscy scrollują, Facebook, Instagram itd. więc paginacja odeszła do lamusa. Czyżby? Tak na prawdę przybrała inną postać. Nieustanne scrollowanie też jest swego rodzaju dostarczaniem danych paczkami, przecież wchodząc na Facebooka nie dostajesz od razu wczytanej całej swojej tablicy, a jedynie fragment. Dzisiaj pokaże Ci jedno wielu rozwiązań implementacji i czym jest sama paginacja w Symfony.

Dla tego posta postawiłam właśnie czystą aplikacje ze szkieletu Symfony. Wystarczyło tylko odpalić:

composer create-project symfony/skeleton blog

Oczywiście doinstalować jeszcze pare innych paczek jak doctrine czy twig.

Czym jest paginacja?

Paginacja to nic innego jak stronicowanie/porcjowanie treści. Bardzo dobrze wpływa na wydajność strony internetowej, ponieważ nie renderuje się od razu wszystkich rekordów, a tylko część przez co czas oczekiwania jest znacznie krótszy. Jest także dobrym rozwiązaniem pod kątem SEO, ponieważ każda ze stron ma osobny adres i pozycjonuje się oddzielnie. Jednak z meta danymi trzeba uważać żeby nie wygenerować stron z takim samy opisem meta i innymi parametrami.

KNP Paginator Bundle

Po co utrudniać sobie życie i wymyślać koło na nowo. Moim zdaniem ważne jest aby umieć sobie ułatwiać życie gotowymi paczkami jednocześnie nie podchodząc do nich bezkrytycznie.

Do paginacji przyda się nam bundle od Knp Labs, instalacja jak zwykle bardzo prosta. Co ważne ten bundle wspiera SEO więc nie musimy się matrwić o to co pisałam powyżej.

composer require knplabs/knp-paginator-bundle

Następnie musimy do skonfigurować, w tym celu należy stworzyć plik config/packages/knp_paginator.yml z treścią

knp_paginator:
  page_range: 3                 # number of links showed in the pagination menu (e.g: you have 10 pages, a page_range of 3, on the 5th page you'll see links to page 4, 5, 6)
  default_options:
    page_name: page             # page query parameter name
    sort_field_name: sort       # sort field query parameter name
    sort_direction_name: direction  # sort direction query parameter name
    distinct: true              # ensure distinct results, useful when ORM queries are using GROUP BY statements
    filter_field_name: filterField  # filter field query parameter name
    filter_value_name: filterValue  # filter value query parameter name
  template:
    pagination: '@KnpPaginator/Pagination/twitter_bootstrap_v4_pagination.html.twig'     # sliding pagination controls template
    sortable: '@KnpPaginator/Pagination/sortable_link.html.twig' # sort link template
    filtration: '@KnpPaginator/Pagination/filtration.html.twig'  # filters template

Zastosowanie

Skoro już mamy zainstalowaną i skonfigurowaną paczkę przyszedł czas aby ją użyć i sprawdzić jej możliwości. Zacznijmy od Controllera.

<?php

declare(strict_types=1);

namespace App\Controller\Controller;


use App\Repository\ArticleRepository;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class ArticleController extends AbstractController
{
    /**
     * @var ArticleRepository
     */
    private $articleRepository;

    /**
     * @var PaginatorInterface
     */
    private $paginator;

    /**
     * @param ArticleRepository  $articleRepository
     * @param PaginatorInterface $paginator
     */
    public function __construct(
        ArticleRepository $articleRepository,
        PaginatorInterface $paginator
    ) {
        $this->articleRepository = $articleRepository;
        $this->paginator = $paginator;
    }

    /**
     * @Route("/article/list/{page}", methods={"GET"})
     */
    public function list(string $page): Response
    {
        $articles = $this->articleRepository->findAll();
        $articles = $this->paginator->paginate($articles, $page, ArticleRepository::PAGE_LIMIT);

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

Controller jest klasyczny, dziedziczy z AbstractControllera, w adnotacji określamy ścieżkę do akcji. W niej pierwsza linia to wywołanie metody z repozytorium, gdzie pobieramy wszystkie artykuły. Potem te rekordy wrzucamy do metody, którą dostarcza nam właśnie KnpPaginatrBundle. Jest on dostępny w tym kontrolerze dzięki DI. Pierwszy parametr to rekordy do paginacji, drugi parametr metody paginate oznacza, którą stronę chcemy wczytać dlatego pobieramy ją z requestu(podajemy ją w ścieżce). Druga liczba to limit elementów na stronę. Umieściłam ją w public const w repozytorium. Polecam takie rozwiązanie, ponieważ paginacja jest trochę jak limit w zapytaniu, więc najodpowiedniejszym miejscem na jej określenie jest właśnie repozytorium.

Szablon w Twigu jest również bardzo prosty, pętla for i wyświetlamy elementy. Dodatkowo musimy wyrenderować nawigację dla tej paginacji. Oprócz nawigacji mamy od razu sortowanie, cudo!

{% extends 'base.html.twig' %}

{% block body %}
    {% if articles is defined and articles|length %}

        <div class="nav text-center">
            {{ knp_pagination_render(articles) }}
        </div>

        <table class="table table-striped">
            <thead>
            <tr>
                <th>{{ knp_pagination_sortable(articles, 'article.id'|trans, 't.id') }}</th>
                <th>{{ knp_pagination_sortable(articles, 'article.title'|trans, 't.title') }}</th>
            </tr>
            </thead>
            <tbody>
            {% for article in articles %}
                <tr>
                    <td>{{ article.id }}</td>
                    <td>{{ article.title }}</td>
                </tr>
            {% endfor %}
            </tbody>
        </table>

        <div class="nav text-center">
            {{ knp_pagination_render(articles) }}
        </div>

    {% else %}
        <p>
            {{ 'empty_list'|trans }}
        </p>
    {% endif %}

{% endblock %}

Tym sposobem bardzo szybko i sprawnie mamy zrobioną paginacje i co jeszcze fajniejsze, jej wykorzystanie w następnych Controllerach jest już mega proste.

Mam nadzieję, że pomogłam i następnym razem będzie Wam prościej. Jeśli macie jeszcze jakieś pytania to piszcie w komentarzach. Chętnie odpowiem i poszerzę artykuł jeśli będziecie tego potrzebować, w końcu dla Was to robię.

Poprzedni artykułTWIG – zmienne, ify, pętle
Następny artykułSymfony Translations

Podobne posty

Jestem programistką, która lubi mieć ręce pełne roboty. Do życia potrzebuje komputera z internetem i kubka gorącej kawy. Więcej na stronie o mnie.

Comments

  1. w akcji list w kontrolerze pobierasz całą listę artykułów. Co w przypadku gdy będzie ich na prawdę dużo? Lepszym sposobem jest przekazanie query buildera z doctrina(jeśli z niego korzystasz). Wtedy artykuły będą się pobierały w takiej ilości w jakiej wyswietlane beda na danej stronie. Pozdro

ZOSTAW ODPOWIEDŹ

Please enter your comment!
Please enter your name here