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

Laravel Cursor Paginator Laravel Package

deepdiver/laravel-cursor-paginator

Cursor-based pagination for Laravel query builder and Eloquent models, offering simple paginators that page through large datasets efficiently without offset queries.

View on GitHub
Deep Wiki
Context7

Getting Started

Before anything else: confirm you actually need this package. It is archived, has zero dependents, and predates Laravel's native cursor pagination (cursorPaginate(), shipped in 8.x). On any Laravel ≥ 8 project, the framework's built-in implementation is the correct choice — it is maintained, handles compound ordering, encrypted cursors, and CursorPaginator JSON shape out of the box. Only reach for this package if you are pinned to an older Laravel (5.x/6.x/7.x) or are maintaining a legacy codebase that already depends on it.

Minimal steps (legacy install only):

  1. composer require deepdiver/laravel-cursor-paginator — check the constraint resolves against your PHP/Laravel version; an archived package will not have widened its illuminate/* constraints, so expect to fork or use --ignore-platform-reqs at your own risk.
  2. Read vendor/deepdiver/laravel-cursor-paginator/ before writing a line of code. There is effectively no documentation, no CI badge, and no issue activity to fall back on — the source and any tests/ directory are your only spec.
  3. Look for a service provider registration in composer.json (extra.laravel.providers), any publishable config, and the public paginator class. That public class is your entire API surface.
  4. First use case: an append-only feed (activity log, message thread, audit trail) where you need WHERE id < ? ORDER BY id DESC LIMIT n semantics with a stable "next" token instead of OFFSET.

A sane first call looks like: build an Eloquent/query-builder instance, hand it to the package's paginator along with the cursor column and direction, then serialize the result. Verify the exact method names against the source — do not trust blog posts, they describe Laravel's native paginator, not this one.

Implementation Patterns

Feed endpoint with an opaque cursor. The whole point of cursor pagination is that the client never sees an offset. Pattern: accept ?cursor=<token>&limit=<n>, return { data: [...], next_cursor: "..." | null }. Keep the token opaque — base64 or encrypt it — so clients treat it as a black box and you stay free to change the underlying column later.

Stable, unique ordering key. Cursor pagination only works if the sort key is unique and indexed. ORDER BY created_at alone is broken the moment two rows share a timestamp — you will silently skip or duplicate rows at page boundaries. Use created_at, id as a compound key, or just id. Always add a composite index matching the sort order ((created_at, id)).

Direction reversal for "previous page". Forward pagination is trivial; backward pagination requires flipping both the ORDER BY and the comparison operator, then reversing the returned rows in memory before responding. Implement this once in a small helper (or a Resource/transformer) rather than duplicating the logic per endpoint.

API Resource wrapping. Don't leak the paginator object to the client. Map it through an JsonResource / ResourceCollection and expose exactly data + next_cursor (and prev_cursor if you implemented reversal). This also isolates you from whatever shape the archived package returns, which matters if you later swap to cursorPaginate().

Chunked background work. A less obvious but high-value pattern: use cursor iteration for exports, reindexing, and backfills instead of chunk(). chunk() with OFFSET degrades linearly on large tables and can skip rows if the underlying data mutates mid-run. Cursor-based iteration is O(1) per page and immune to that class of bug. The package's cursor generator is a reasonable fit here even if you never expose it over HTTP.

Integration with Blade/Inertia. Resolve the cursor from the request query string in a form request, pass the resulting collection + next_cursor to the view, and have the frontend store next_cursor in component state. Never reconstruct the cursor client-side from an ID — you lose the ability to change the encoding.

Gotchas and Tips

  • Archived means no security patches. This is the headline risk. Cursor tokens are user-supplied input; if the package decodes them into SQL predicates, audit that path yourself for injection and for cursor-forgery (a client crafting a cursor to read another tenant's rows). Add a signing/HMAC layer or encrypt the cursor if it isn't already.
  • No total(). By design there is no count — that's the performance win. If a UI needs "page 3 of 47" or a progress bar, cursor pagination is the wrong tool; use paginate() or precompute counts.
  • No random access. You cannot jump to page N, and that is not a bug to work around. Design the UI as infinite scroll or "load more" rather than numbered links.
  • composer update will eventually break it. The archived package's illuminate/support constraint is frozen. Expect conflicts when you bump Laravel. Mitigation: fork it into your org, or copy the ~100 lines you actually use into app/Support/ and delete the dependency.
  • Null and non-comparable values in the cursor column. If the sort column is nullable, every comparison (<, >) against NULL silently returns no rows, and your feed truncates. Guarantee NOT NULL, or add explicit null-ordering handling.
  • Debugging skipped/duplicated rows: reproduce deterministically by inserting rows with identical timestamps and paging through. If a row appears twice or vanishes, the sort key is not unique. This is the single most common failure mode.
  • Extension point: rather than subclassing the archived paginator, wrap it. A thin App\Support\CursorFeed service that owns token encoding, tenant scoping, and direction reversal gives you a one-file migration path to native cursorPaginate() later.
  • Migration tip: when you do upgrade, Model::query()->orderBy('id')->cursorPaginate($perPage) replaces this package almost verbatim, and cursorPaginate accepts the same encoded cursor via ?cursor=. Write your endpoints against your own response shape now, and the swap becomes a two-line change inside CursorFeed instead of a client-facing breaking change.
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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin