silpo-tech/paginator-bundle
Paginator Bundle from Silpo Tech provides flexible pagination for PHP and Laravel applications, splitting large result sets into manageable pages for list views and search results. It offers straightforward Composer installation, simple configuration, and integration-ready APIs for cleaner listing c...
This is a Symfony bundle, not a Laravel package — there's no service provider, no facade, and no config/paginator.php. If you're coming from LengthAwarePaginator, expect to wire it into Symfony's container and (almost certainly) Doctrine rather than Eloquent.
Minimal steps:
composer require silpo-tech/paginator-bundle — note the last release is recent (Oct 2025) but the project has zero stars and zero dependents, so treat it as a small internal library that happens to be public.config/bundles.php (SilpoTech\PaginatorBundle\PaginatorBundle::class => ['all' => true] — verify the exact namespace from composer.json's autoload.psr-4 before guessing).src/DependencyInjection/ for the extension alias used in YAML — that alias (not the vendor name, usually) is what you configure under.src/ — at this size the whole package is probably a few classes, and reading them is faster than trusting docs.First use case: a controller action that takes ?page=2&perPage=25 from the request, hands a Doctrine QueryBuilder to the paginator service, and returns a page of results plus metadata. Build that end-to-end before adopting it anywhere else — it will surface whether the package gives you a paginator object, a result set, or just a helper for building LIMIT/OFFSET.
Keep it in the service layer, not controllers. Inject the paginator service into a repository or a query object and return a small result DTO (items, page, perPage, total, lastPage). That makes it trivial to render in Twig and serialize in JSON without duplicating page math.
Clamp pagination input at the boundary. Read page and perPage from the request, cast to int, and clamp (max(1, $page), min($perPage, 100)). Never let a caller request perPage=100000 — OFFSET that large is a free DoS vector against your database.
Two response shapes, one source of truth. For HTML, pass the result object to Twig and render links that preserve existing query params ({{ path(app.request.attributes.get('_route'), app.request.query.all|merge({page: p})) }}). For APIs, map the same result object to a JSON envelope with links.next/links.prev built from the current URL. Doing both from one DTO keeps page numbering consistent across web and API.
Consider a trait or abstract controller method for extracting pagination params, so every list endpoint behaves identically. Small bundles like this usually don't ship that ergonomics layer — you're expected to build it.
If you're actually staying in Laravel: don't adopt this. Laravel's paginate() on a query builder is more capable and already framework-native. The only justification is a shared monorepo or a service boundary where a Symfony component must produce pagination metadata consumed by a Laravel app — and then you'd be better off defining the metadata contract yourself than coupling to this bundle.
COUNT(*) query is the cost. Any Doctrine-backed paginator runs a count query plus the page query. On large or heavily-joined tables this dominates. Check whether the bundle supports an explicitly-provided count or a "no total" mode; if not, you may need to bypass it for hot endpoints.total is 0.composer.json, and keep the coupling thin — wrap the service behind your own interface so you can swap in a hand-rolled LIMIT/OFFSET or Pagerfanta in an hour if it's abandoned.per_page or max page size are read from the container, they're easy to miss because the bundle may silently fall back. Grep src/ for the parameter names and set them explicitly rather than relying on documented defaults.bin/console debug:container | grep -i pagin and bin/console config:dump-reference <alias>. Nine times out of ten the bundle wasn't registered in config/bundles.php or the extension alias was wrong.How can I help you explore Laravel packages today?