Learn how to paginate a Doctrine collection using the KnpPaginatorBundle in Symfony 4.

Performance is an important feature of every web application. One of the most necessary things in applications that handle large sets of Data, is the implementation of a paginator. So the user can navigate through the collection of data without loading thousand of items at once. In Symfony 3, the KnpPaginatorBundle is the perfect solution when you work with Doctrine, however with the introduction of Symfony 4, the installation and configuration of this utility isn't well documented and most of the developers will end up implementing another package for sake of simplicity. But don't worry, the installation of this bundle is a piece of cake if you follow our steps. KnpPaginatorBundle can paginate:

  • array
  • Doctrine\ORM\Query
  • Doctrine\ORM\QueryBuilder
  • Doctrine\ODM\MongoDB\Query\Query
  • Doctrine\ODM\MongoDB\Query\Builder
  • Doctrine\ODM\PHPCR\Query\Query
  • Doctrine\ODM\PHPCR\Query\Builder\QueryBuilder
  • Doctrine\Common\Collection\ArrayCollection - any doctrine relation collection including
  • ModelCriteria - Propel ORM query
  • array with Solarium_Client and Solarium_Query_Select as elements

In this article, we'll show you how to install the widely know KnpPaginatorBundle in your Symfony 4 based project.

1. Install and configure KnpPaginatorBundle

The KnpPaginatorBundle is a package for Symfony that allows you to use a SEO friendly Symfony paginator to paginate everything. This utility:

  • Does not require initializing specific adapters
  • Can be customized in any way needed, etc.: pagination view, event subscribers.
  • Possibility to add custom filtering, sorting functionality depending on request parameters.
  • Separation of concerns, paginator is responsible for generating the pagination view only, pagination view - for representation purposes.

In order to install it in your Symfony project, you can install its latest version with composer using the following command:

composer require knplabs/knp-paginator-bundle

This will install the bundle and register it automatically on the app/config/bundles.php file. For more information about this package, please visit the official repository at Github here.

After installing the package, you only need to create the configuration file of the paginator in the app/config/packages directory. This file is the knp_paginator.yaml that contains the configuration of this utility. The configuration is up to you, but you can use a default configuration providen by the documentation:

# config/packages/paginator.yaml
    page_range: 5                       # 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)
        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 paameter name
        pagination: '@KnpPaginator/Pagination/sliding.html.twig'     # sliding pagination controls template
        sortable: '@KnpPaginator/Pagination/sortable_link.html.twig' # sort link template
        filtration: '@KnpPaginator/Pagination/filtration.html.twig'  # filters template

Save the file, clear the cache of your project.

2. Configure Paginator in Controller

Once the plugin is installed and configured, it's usage is pretty simple. Just be sure that your controllers extend the Symfony\Bundle\FrameworkBundle\Controller\Controller class as this is the one that allows you to access multiple services with the get method of the class. Just instantiate the paginator and keep it on a variable, then create some Doctrine query (important Query, not results) and then call the paginate method from the paginator and provide its 3 required arguments:

  1. The Doctrine Query
  2. The current page, defined by the get parameter "page" and set the first page as default when this parameter doesn't exist.
  3. The limit of items per page.

You need to iterate over the returned element of the paginate method. This object contains the results of your query with the limit of results and other properties that you can access later in the view:


namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use App\Entity\Appointments;

// Include paginator interface
use Knp\Component\Pager\PaginatorInterface;

class DefaultController extends AbstractController
    // Include the paginator through dependency injection, the autowire needs to be enabled in the project
    public function index(Request $request, PaginatorInterface $paginator)
        // Retrieve the entity manager of Doctrine
        $em = $this->getDoctrine()->getManager();
        // Get some repository of data, in our case we have an Appointments entity
        $appointmentsRepository = $em->getRepository(Appointments::class);
        // Find all the data on the Appointments table, filter your query as you need
        $allAppointmentsQuery = $appointmentsRepository->createQueryBuilder('p')
            ->where('p.status != :status')
            ->setParameter('status', 'canceled')
        // Paginate the results of the query
        $appointments = $paginator->paginate(
            // Doctrine Query, not results
            // Define the page parameter
            $request->query->getInt('page', 1),
            // Items per page
        // Render the twig view
        return $this->render('default/index.html.twig', [
            'appointments' => $appointments

Now that you return the result of the pagination, you can access this object in the Twig view that will rendered as response of this action in our controller.

3. Create paginator in Twig view

As shown in the previous step, we sent the pagination object to a Twig view that renders our pagination slider and the items. In our example, the content of our Twig view is the following:

{# ./templates/default/index.html.twig #}
<!DOCTYPE html>
        <meta charset="UTF-8">
        <title>My Application</title>
        {% for appointment in appointments %}
                {{ appointment.title }}
        {% endfor %}
        <div class="navigation">
            {{ knp_pagination_render(appointments) }}

This will generate a list of 5 items on every page as indicated in our controller. The knp_pagination_render function renders a slider with all the available pages of the items. When clicking on every generated link, the same URL with all its query parameters will be used as well and the new parameter namely page that indicated our controller which items to return to the view.

Happy coding !

Senior Software Engineer at Software Medico. Interested in programming since he was 14 years old, Carlos is a self-taught programmer and founder and author of most of the articles at Our Code World.