Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Pagination Bundle Laravel Package

gpslab/pagination-bundle

Symfony bundle integrating the gpslab/pagination library: registers pagination services, Twig helpers, and configuration so you can paginate Doctrine queries, arrays, and custom data sources with reusable page objects and simple templates.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: it is a Symfony bundle, not a Laravel package. gpslab/pagination-bundle ships a Symfony DI extension, a Twig extension, and a container service named gpslab_pagination.paginator. None of that boots in Laravel. The bundle has ~11 stars, zero dependents, and its last release was March 2020 — treat it as a reference implementation, not a dependency.

What you actually consume from Laravel is its framework-agnostic core, gpslab/pagination, which the bundle wraps. That library gives you two plain objects: a Paginator (computes page math) and a Page (immutable value object holding number, total, per-page, offset/limit).

Minimal steps:

composer require gpslab/pagination

Then, as your first use case, replace a hand-rolled ?page= calculation in a controller:

use GPSLab\Pagination\Paginator;

Route::get('/posts', function (Request $request, Paginator $paginator) {
    $perPage = 20;
    $current = max(1, (int) $request->query('page', 1));

    $total = Post::where('published', true)->count();
    $page  = $paginator->paginate($total, $current, $perPage);

    $posts = Post::where('published', true)
        ->orderByDesc('id')
        ->offset($page->getOffset())
        ->limit($page->getLimit())
        ->get();

    return view('posts.index', compact('posts', 'page'));
});

Where to look first in the repo:

  1. Paginator.php / Page.php — the only classes you will realistically use; read them end to end, they are short.
  2. Resources/views/ (the Twig pagination template) — useful as a layout reference for the markup you will rewrite in Blade.
  3. DependencyInjection/Configuration.php — shows the only extension point the bundle exposes (the default template name).
  4. Resources/config/services.yaml — shows exactly how thin the bundle is; it is ~one service and one Twig function.

If you are genuinely in a Symfony app, composer require gpslab/pagination-bundle, enable it in bundles.php, and autowire GPSLab\Pagination\Paginate into a controller. That is the intended path — and the reason the package is a poor fit for a Laravel codebase.

Implementation Patterns

Pattern 1 — count-then-slice (the only pattern this library supports). Paginator is pure arithmetic; it never touches a data source. You are responsible for the COUNT(*) and the OFFSET/LIMIT. This is fine for admin tables under a few hundred thousand rows and a liability above that (see Gotchas).

Pattern 2 — wrap it in a service so controllers stay clean.

namespace App\Support;

use GPSLab\Pagination\Page;
use GPSLab\Pagination\Paginator;
use Illuminate\Database\Eloquent\Builder;

final class TablePaginator
{
    public function __construct(private Paginator $paginator) {}

    public function paginate(Builder $query, int $page, int $perPage = 25): array
    {
        $total = (clone $query)->toBase()->getCountForPagination();
        $p = $this->paginator->paginate($total, max(1, $page), $perPage);

        return [
            'items' => $query->offset($p->getOffset())->limit($p->getLimit())->get(),
            'page'  => $p,
        ];
    }
}

Note getCountForPagination() rather than count() — it keeps existing joins/group-bys cheaper and is what Laravel's own paginator uses internally.

Pattern 3 — bridge to Laravel's view layer. The bundle's Twig template is useless to you; keep the Page object but feed it into a Blade component, or convert to LengthAwarePaginator so $items->links() and appends() keep working for free:

$lara = new \Illuminate\Pagination\LengthAwarePaginator(
    $items, $page->getTotal(), $page->getPerPage(), $page->getNumber(),
    ['path' => request()->url(), 'query' => request()->query()]
);

This conversion is the pragmatic move: you get the math object you wanted plus the Bootstrap/Tailwind views Laravel already ships.

Pattern 4 — JSON APIs. Serialize the Page metadata explicitly instead of relying on Eloquent's toArray(). A stable shape (current_page, per_page, total, last_page, from, to) matters more than which class produced it; clients that later move to cursorPaginate should not break.

Pattern 5 — Symfony (only if you are actually there). Autowire Paginate, call paginate($total, $page, $perPage) in the controller, and render with the Twig helper the bundle registers (a paginate_render(page, template) style function — verify the exact signature in Twig/). Configuration is a single key for the default template; extend by passing a custom template name rather than by subclassing.

Pattern 6 — extension via decoration. If you need clamping, page-size allowlists, or a different default, decorate Paginate in a service provider rather than forking. It is a one-method interface, which is the one genuinely nice property of this package.

Gotchas and Tips

  • Do not composer require gpslab/pagination-bundle in Laravel. Nothing will auto-discover, and Symfony\Bundle\FrameworkBundle / Twig\Extension\AbstractExtension may not be installed. Pull in gpslab/pagination only.
  • Name collision. GPSLab\Pagination\Paginator and Illuminate\Pagination\Paginator share a short name; never import both unqualified in one file. Alias one at the top.
  • Unmaintained since 2020. No PHP 8.x guarantees in the original CI matrix; the code is trivial enough that it will likely run, but pin ^1.0 (or whatever tag you verify) and cover your usage with a test. Assume no upstream fixes.
  • Out-of-range pages are your problem. Page 0, negative pages, and pages past the end are not clamped for you. Clamp on input (max(1, ...)) and treat an empty result on an over-large page as a normal empty state — or 404 it deliberately.
  • Zero rows. Verify what the library returns for paginate(0, 1, 20) — total pages is typically 1 or 0 depending on version, and a last_page of 0 will produce /page/0 links. Guard your view.
  • per_page from the query string is an attack surface. An unrestricted ?per_page=100000 will happily LIMIT 100000. Allowlist sizes.
  • The count query is the bottleneck. With OFFSET pagination on large tables you pay for the COUNT(*) plus a deep offset scan on every request. Cache the count for a minute, or switch to Laravel's cursorPaginate for infinite-scroll surfaces and keep this only for numbered admin tables.
  • No query-builder integration whatsoever. Do not expect a ->paginate() macro; you own the count, orderBy, and offset/limit. Missing a deterministic orderBy with an offset produces duplicated rows across pages.
  • Twig templates are not portable. The bundle's view is a starting point for markup only; write a Blade component and forget the Twig file exists.
  • Debugging tips. Since the classes are pure, dump $page->getTotalPages(), getNumber(), getOffset(), getLimit() at the boundary; nine out of ten pagination bugs are a wrong total (forgotten where) or an off-by-one page, both visible in that one dump.
  • Extension points worth knowing. The Paginate interface (swap/decorate it), and the bundle's single template config key. There is no event system, no driver interface, and no Blade/Twig renderer abstraction to hook into — budget accordingly if you were hoping for a pluggable renderer.
  • Honest verdict for a Laravel shop. Use this only if you want a framework-agnostic Page value object in a shared domain layer, or you are porting a Symfony service. Otherwise Laravel's paginate()/cursorPaginate() plus LengthAwarePaginator covers the same ground with maintained code, Blade views included, and no 2020-era dependency.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment