Installation
composer require knplabs/knp-paginator-bundle
Ensure KnpPaginatorBundle is enabled in config/bundles.php (auto-enabled in Symfony Flex).
Basic Pagination in Controller
use Knp\Component\Pager\PaginatorInterface;
public function index(PaginatorInterface $paginator, EntityManagerInterface $em)
{
$products = $em->getRepository(Product::class)->findAll();
$pagination = $paginator->paginate(
$products,
$request->query->getInt('page', 1), // Page number
10 // Items per page
);
return $this->render('product/index.html.twig', ['pagination' => $pagination]);
}
First Use Case: Twig Rendering Use the default Twig template:
{{ knp_pagination_render(pagination) }}
Or customize with:
{% for product in pagination %}
{{ product.name }}
{% endfor %}
Query-Based Pagination (ORM/Doctrine)
$query = $em->getRepository(Product::class)->createQueryBuilder('p')
->where('p.price > :min')
->setParameter('min', $request->query->get('min_price'))
->getQuery();
$pagination = $paginator->paginate(
$query,
$request->query->getInt('page', 1),
12,
['filter' => 'price_asc'] // Route parameters
);
Collection Pagination (Non-Query Data)
$collection = $service->getFilteredProducts();
$pagination = $paginator->paginate(
$collection,
$request->query->getInt('page', 1),
10
);
Dynamic Sorting
$sortField = $request->query->get('sort', 'name');
$sortDir = $request->query->get('dir', 'ASC');
$queryBuilder = $em->getRepository(Product::class)->createQueryBuilder('p')
->orderBy("p.$sortField", $sortDir);
Integration with Forms
{{ form_start(form) }}
{{ form_widget(form) }}
{{ knp_pagination_render(pagination) }}
{{ form_end(form) }}
templates/KnpPaginator/ or use {% extends 'KnpPaginator:SlidingPaginationSliding.html.twig' %}.Knp\Paginator\Event\PaginationEvent.return $this->json([
'data' => $pagination,
'meta' => [
'total' => $pagination->getTotalItemCount(),
'pages' => $pagination->getPageCount(),
]
]);
Query vs. Collection:
paginate() with a Query instance.Query wrapper).InvalidArgumentException if mixing types.Route Parameters:
route and routeParameters in paginate() match your route (e.g., ['filter' => $request->query->all()]).useQueryString: true to preserve existing query params:
$paginator->paginate($query, $page, 10, [], ['useQueryString' => true]);
Twig Template Overrides:
php bin/console cache:clear).var/cache/dev/ for compiled templates if styles break.Performance:
findAll()). Use COUNT() or getResult() with DISTINCT for large datasets.setMaxResults() and setFirstResult() manually for complex queries.# config/packages/dev/doctrine.yaml
doctrine:
dbal:
logging: true
profiling: true
dump($pagination->getCurrentPageNumber(), $pagination->getItemCountOnPage());
Knp\Paginator\Event\PaginationEvent to inspect pre/post-pagination data.Custom Adapters:
Knp\Component\Pager\Adapter\AdapterInterface for non-Doctrine data sources (e.g., Elasticsearch).Template Variables:
pagination object:
{{ pagination.hasPreviousPage() ? link_to_route('product_index', 'Previous') : '' }}
Configuration:
config/packages/knp_paginator.yaml:
knp_paginator:
page_range: 5 # Number of page links shown
default_range: 10 # Items per page
template:
pagination: 'custom_template.html.twig'
Symfony UX Integration:
<div data-controller="pagination" data-pagination-url="{{ path('product_index') }}">
{{ knp_pagination_render(pagination) }}
</div>
How can I help you explore Laravel packages today?