Symfony pagination example

Symfony pagination example banner

The easiest way to create pagination in your Symfony app is by using third-party libraries. The recommended and most used library for this purpose is KnpPaginatorBundle.

Symfony pagination using Knp Paginator Bundle

Install the bundle and configure it by running

composer require knplabs/knp-paginator-bundle

If you are using Symfony 4.4 or 5.4, it should be configured automatically thanks to Symfony Flex.

Let's take a look at this pagination example

Controller

public function listAction(EntityManagerInterface $em, PaginatorInterface $paginator, Request $request)
{
    $dql   = "SELECT a FROM App:Post p";
    $query = $em->createQuery($dql);

    $pagination = $paginator->paginate(
        $query, /* query NOT result */
        $request->query->getInt('page', 1), /*page number*/
        10 /*limit per page*/
    );

    // parameters to template
    return $this->render('post/list.html.twig', ['pagination' => $pagination]);
}

PaginatorInterface->paginate() expects doctrine query, page number, and limit per page. This example is only for Doctrine pagination.

View

{# total items count #}
<div class="count">
    {{ pagination.getTotalItemCount }}
</div>
<table>
    {% for post in pagination %}
        <tr {% if loop.index is odd %}class="color"{% endif %}>
            <td>{{ post.id }}</td>
            <td>{{ post.title }}</td>
            <td>{{ post.date | date('Y-m-d') }}, {{ post.time | date('H:i:s') }}</td>
        </tr>
    {% endfor %}
</table>
{# display navigation #}
<div class="navigation">
    {{ knp_pagination_render(pagination) }}
</div>

It's as easy as that. No additional code is needed.

You can also customize how the pagination html is rendered. KnpPaginatorBundle gives you a lot of predefined options for different CSS frameworks.

For example, if you use Bootstrap and you want to leverage the pagination component, you can tell Knp Paginator to render pagination with bootstrap classes.

Create config/packages/knp_paginator.yaml with the following contents 

knp_paginator:
    page_range: 5                       # number of links shown 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/bootstrap_v5_pagination.html.twig' # Use bootstrap pagination

For more information and documentation, you can check the official GitHub docs of the component.

The example should work for Symfony 4, 5 and 6.

Symfony Doctrine Pagination example

Doctrine has built-in Paginator implementation, using the Doctrine\ORM\Tools\Pagination\Paginator . This can be considered as a custom pagination.

Let's take a look at how it works.

use Doctrine\ORM\Tools\Pagination\Paginator;

// ..
    #[Route('/view_posts', name: 'view_posts')]
    public function action(Request $request)
    {
        $em = $this->getDoctrine()->getManager();
        $repo = $em->getRepository(Post::class);

        // Query and not Result!
        $query = $repo->getAllPostsQuery();

        $limit = 50;
        $page = $request->get('page', 1);
        $paginator = new Paginator($query);
        $paginator
            ->getQuery()
            ->setFirstResult($limit * ($page - 1))
            ->setMaxResults($limit)
        ;
        $total = $paginator->count();
        $lastPage = (int) ceil($total / $limit);

        return $this->render('archive/view.html.twig', [
            'paginator' => $paginator,
            'total' => $total,
            'lastPage' => $lastPage,
            'page' => $page,
        ]);
    }
// ..

Pagination in twig

<hr>
    {% for post in paginator %}
    <!-- render post here -->
    {% endfor %}

    {% set _currentRoute = app.request.attributes.get('_route') %}
    {% set _currentParams = app.request.query.all|merge(app.request.attributes.get('_route_params')) %}

    {% if lastPage > 1 %}
        <nav>
            <ul class="pagination justify-content-center">
                <li class="page-item{{ page <= 1 ? ' disabled' : '' }}">
                    <a class="page-link" href="{{ path(_currentRoute, _currentParams|merge({page: page - 1})) }}" aria-label="Previous">
                        &laquo; Previous
                    </a>
                </li>
                {% for i in lastPage %}
                    <li class="page-item {% if i == page %}active{% endif %}">
                        <a class="page-link" href="{{ path(_currentRoute, _currentParams|merge({page: i})) }}">{{ i }}</a>
                    </li>
                {% endfor %}
                <li class="page-item {{ page >= lastPage ? ' disabled' : '' }}">
                    <a class="page-link" href="{{ path(_currentRoute, _currentParams|merge({page: page + 1})) }}" aria-label="Next">
                        Next &raquo;
                    </a>
                </li>
            </ul>
        </nav>
    {% endif %}

You could extract the pagination logic into a service itself so that you can reuse the code.

Conclusion

Knp Paginator Bundle requires a little bit more configuration, but it has a lot more features, like sorting, different templates, etc. On the other hand, the Doctrine pagination is probably enough for most of the use cases, and it doesn't require additional libraries and configuration.