Symfony pagination example
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">
« 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 »
</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.