[{"data":1,"prerenderedAt":38},["ShallowReactive",2],{"$f3nvmkafcs6eml":3,"$fi8d7o0dwhxcy":27},{"content":4,"canonicalPath":5,"package":6,"version":26},"# Cursor Pagination\n\n::div{class=\"docs-note docs-note--new-feature\"}\nCursor pagination was introduced in Pagerfanta 4.10.\n::\nPagerfanta supports two pagination strategies:\n\n- **Offset pagination** (the `Pagerfanta\\Pagerfanta` class) identifies a page by its number, and fetches the items for a page by skipping the items on the pages before it. It always knows the total number of items and pages, so it can link to any page.\n- **Cursor pagination** (also known as keyset pagination) identifies a page by a cursor pointing to an item, and fetches the items after (or before) that item. It performs consistently on large lists and is not affected by items being added or removed on earlier pages, but it can only link to the previous and next pages.\n\n## Pager Interfaces\n\nBoth strategies share a common set of interfaces, so code such as views and serializers can work with any pager.\n\n| Interface                            | Description                                                                                                               |\n|--------------------------------------|---------------------------------------------------------------------------------------------------------------------------|\n| `Pagerfanta\\PagerInterface`          | The root pager API: the current page results, the max per page, and whether (and where) there are previous and next pages |\n| `Pagerfanta\\CountablePagerInterface` | A pager which knows the total number of results with `getNbResults()`                                                     |\n| `Pagerfanta\\OffsetPagerInterface`    | A countable pager using offset pagination, implemented by `Pagerfanta\\Pagerfanta`                                         |\n| `Pagerfanta\\CursorPagerInterface`    | A pager using cursor pagination                                                                                           |\n\nThe previous and next pages of any pager are described by a `Pagerfanta\\Position\\Position`, which is either a `Pagerfanta\\Position\\PagePosition` (holding a page number) or a `Pagerfanta\\Position\\CursorPosition` (holding a cursor). Use the `hasPreviousPage()` and `hasNextPage()` methods before calling `getPreviousPosition()` or `getNextPosition()`, which throw a `Pagerfanta\\Exception\\LogicException` if there is no page in that direction.\n\n```php\n\u003C?php\n\nuse Pagerfanta\\PagerInterface;\n\nfunction describe(PagerInterface $pager): void\n{\n    foreach ($pager->getCurrentPageResults() as $item) {\n        \u002F\u002F ...\n    }\n\n    if ($pager->hasNextPage()) {\n        $position = $pager->getNextPosition(); \u002F\u002F A PagePosition or a CursorPosition\n    }\n}\n```\n\n## Creating A Cursor Pager\n\nCursor pagers are created with a cursor adapter (see the [available adapters](\u002Fopen-source\u002Fpackages\u002Fpagerfanta\u002Fdocs\u002F4.x\u002Fadapters)), the maximum number of items per page, and the position of the current page (or null for the first page).\n\nThe `Pagerfanta\\CursorPagerfantaFactory` creates the right pager for the adapter: a `Pagerfanta\\CountableCursorPagerfanta` if the adapter can count its results (it implements `Pagerfanta\\Adapter\\CountableAdapterInterface`), otherwise a `Pagerfanta\\CursorPagerfanta`. Check for `Pagerfanta\\CountablePagerInterface` to find out whether the total number of results is available.\n\n```php\n\u003C?php\n\nuse Pagerfanta\\Adapter\\ArrayCursorAdapter;\nuse Pagerfanta\\CountablePagerInterface;\nuse Pagerfanta\\CursorPagerfantaFactory;\n\n$adapter = new ArrayCursorAdapter($posts, static fn (array $post): array => ['id' => $post['id']]);\n\n$pager = CursorPagerfantaFactory::create($adapter, 10);\n\nif ($pager instanceof CountablePagerInterface) {\n    $pager->getNbResults(); \u002F\u002F The total number of posts\n}\n```\n\n::div{class=\"docs-note\"}\nUnlike the \u003Ccode>Pagerfanta\u003C\u002Fcode> class, the \u003Ccode>count()\u003C\u002Fcode> method of the cursor pagers returns the number of items on the current page. Use \u003Ccode>getNbResults()\u003C\u002Fcode> on a countable pager for the total number of results.\n::\nCursor pagers are immutable. To move to another page, create a new pager for its position with the `withPosition()` method, which keeps the adapter and the maximum number of items per page.\n\n```php\n\u003C?php\n\nif ($pager->hasNextPage()) {\n    $nextPager = $pager->withPosition($pager->getNextPosition());\n}\n```\n\n### Totals Are Opt-In\n\nCounting the results of a query is often the most expensive part of paginating it, and cursor pagination does not need the total to know whether there is another page. Cursor adapters therefore do not count their results unless they implement `Pagerfanta\\Adapter\\CountableAdapterInterface`, and a countable pager only counts the results when `getNbResults()` is called.\n\nTo add a total to any cursor adapter, decorate it with the `Pagerfanta\\Adapter\\CountingCursorAdapter`, using either a callable returning the count or another adapter (such as the offset adapter for the same data source) to count with.\n\n```php\n\u003C?php\n\nuse Pagerfanta\\Adapter\\CountingCursorAdapter;\nuse Pagerfanta\\Doctrine\\DBAL\\CursorQueryAdapter;\nuse Pagerfanta\\Doctrine\\DBAL\\SingleTableQueryAdapter;\nuse Pagerfanta\\Doctrine\\DBAL\\SortColumn;\n\n$adapter = new CountingCursorAdapter(\n    new CursorQueryAdapter($queryBuilder, [new SortColumn('p.id')]),\n    new SingleTableQueryAdapter($queryBuilder, 'p.id'),\n);\n```\n\n### Backward Navigation\n\nSome data sources can only move forward through a cursor (for example, Solr's cursorMark). A cursor pager reports this with its `supportsBackwardNavigation()` method, and a pager which does not support backward navigation never has a previous page. The views omit the previous link entirely for these pagers.\n\n### Auto-Pagination\n\nThe cursor pagers support [auto-pagination](\u002Fopen-source\u002Fpackages\u002Fpagerfanta\u002Fdocs\u002F4.x\u002Fusage#auto-pagination), iterating over the items on every page from the current position onwards. As the pagers are immutable, the pager is not changed by the iteration.\n\n```php\n\u003C?php\n\nforeach ($pager->autoPagingIterator() as $item) {\n    \u002F\u002F Iterate over each item from all pages of the result set\n}\n```\n\n## Cursors\n\nA `Pagerfanta\\Cursor\\Cursor` holds the sort key values of the item to paginate from, keyed by the sort key (such as `['p.createdAt' => '2026-09-25 12:00:00', 'p.id' => 42]`), and the `Pagerfanta\\Cursor\\Direction` to paginate in (`Direction::Next` or `Direction::Previous`). The values must be scalars or null.\n\n### Encoding Cursors\n\nCursors are converted to and from strings, such as for a query string parameter in a URL, with a `Pagerfanta\\Cursor\\CursorEncoderInterface`. The library provides the `Pagerfanta\\Cursor\\Base64JsonCursorEncoder`, which encodes cursors as URL-safe Base64 JSON strings.\n\n```php\n\u003C?php\n\nuse Pagerfanta\\Cursor\\Base64JsonCursorEncoder;\nuse Pagerfanta\\Exception\\InvalidCursorException;\nuse Pagerfanta\\Position\\CursorPosition;\n\n$encoder = new Base64JsonCursorEncoder();\n\n\u002F\u002F Encode the cursor for the next page into a URL\n$url = '\u002Fposts?cursor=' . $encoder->encode($pager->getNextPosition()->cursor);\n\n\u002F\u002F Decode the cursor from the request\ntry {\n    $position = isset($_GET['cursor']) ? new CursorPosition($encoder->decode($_GET['cursor'])) : null;\n} catch (InvalidCursorException) {\n    \u002F\u002F Respond with a 400 Bad Request error\n}\n```\n\n::div{class=\"docs-note\"}\nThe \u003Ccode>Base64JsonCursorEncoder\u003C\u002Fcode> does not sign the cursors it encodes, so a client can decode and alter a cursor to paginate from any position allowed by the underlying query. The adapters validate that a cursor matches their sort fields before using it, but if your application needs to prevent tampering, decorate the encoder with one which signs the payload.\n::\n## Rendering Cursor Pagers\n\nCursor pagers can only link to the previous and next pages, so they are rendered with a sequential view instead of a view with numbered page links. See the [views documentation](\u002Fopen-source\u002Fpackages\u002Fpagerfanta\u002Fdocs\u002F4.x\u002Fviews#sequential-views) for details, and the [route generator documentation](\u002Fopen-source\u002Fpackages\u002Fpagerfanta\u002Fdocs\u002F4.x\u002Froute-generator#position-route-generators) for generating the URLs for cursors.\n","\u002Fopen-source\u002Fpackages\u002Fpagerfanta\u002Fdocs\u002F4.x\u002Fcursor-pagination",{"name":7,"slug":8,"description":9,"github":10,"packagistName":12,"packageType":13,"hasDocumentation":14,"supported":14,"visible":14,"versions":15},"Pagerfanta","pagerfanta","Pagination library for PHP applications with support for several data providers",{"owner":11,"repo":7},"BabDev","pagerfanta\u002Fpagerfanta","php-package",true,[16,19,21,23],{"version":17,"released":18},"5.x",false,{"version":20,"released":14},"4.x",{"version":22,"released":14},"3.x",{"version":24,"released":14,"endOfSupport":25},"2.x","2022-03-31T23:59:59.999Z",{"version":20,"released":14},{"content":28,"canonicalPath":29,"package":30,"version":37},"- [Introduction](\u002Fopen-source\u002Fpackages\u002Fpagerfanta\u002Fdocs\u002F4.x\u002Fintro)\n- [Installation & Setup](\u002Fopen-source\u002Fpackages\u002Fpagerfanta\u002Fdocs\u002F4.x\u002Finstallation)\n- [Usage](\u002Fopen-source\u002Fpackages\u002Fpagerfanta\u002Fdocs\u002F4.x\u002Fusage)\n- [Pagination Adapter](\u002Fopen-source\u002Fpackages\u002Fpagerfanta\u002Fdocs\u002F4.x\u002Fadapter)\n- [Available Adapters](\u002Fopen-source\u002Fpackages\u002Fpagerfanta\u002Fdocs\u002F4.x\u002Fadapters)\n- [Cursor Pagination](\u002Fopen-source\u002Fpackages\u002Fpagerfanta\u002Fdocs\u002F4.x\u002Fcursor-pagination)\n- [Views](\u002Fopen-source\u002Fpackages\u002Fpagerfanta\u002Fdocs\u002F4.x\u002Fviews)\n- [Templates](\u002Fopen-source\u002Fpackages\u002Fpagerfanta\u002Fdocs\u002F4.x\u002Ftemplates)\n- [Route Generator](\u002Fopen-source\u002Fpackages\u002Fpagerfanta\u002Fdocs\u002F4.x\u002Froute-generator)\n- [Framework Integrations](\u002Fopen-source\u002Fpackages\u002Fpagerfanta\u002Fdocs\u002F4.x\u002Fframework-integrations)\n","\u002Fopen-source\u002Fpackages\u002Fpagerfanta\u002Fdocs\u002F4.x\u002Findex",{"name":7,"slug":8,"description":9,"github":31,"packagistName":12,"packageType":13,"hasDocumentation":14,"supported":14,"visible":14,"versions":32},{"owner":11,"repo":7},[33,34,35,36],{"version":17,"released":18},{"version":20,"released":14},{"version":22,"released":14},{"version":24,"released":14,"endOfSupport":25},{"version":20,"released":14},1791489122502]