postmill / pagerwave
Key-set pagination for PHP
Requires
- php: 7.2.*|7.3.*|7.4.*|8.0.*|8.1.*|8.2.*|8.3.*|8.4.*
Requires (Dev)
- phpunit/phpunit: ^8.5
README
PagerWave is a pagination library for PHP.
It differs from most pagination libraries in that it uses key sets to keep track of the current location. Where traditional paginators use a page number to calculate an offset into your collection, PagerWave fetches n + 1 items when paging, then uses the n + 1th item to determine the starting point of the next page.
This difference is most obvious in the generated URLs. Offset-based paginators
will give you a URL like /posts/1
, while PagerWave will give you a URL like
/posts/?next[date]=2020-04-20&[id]=455
. While uglier, this has several
performance and practical benefits compared to offset-based pagination:
The items on a page won't change as you add new items. This makes it ideal for infinite scroll scenarios, as adding or deleting items while someone is scrolling won't cause duplicate or skipped items, respectively.
It is fast with large databases, as key sets can take advantage of column indexes. Offset-based pagination, on the other hand, does not scale well as the database grows.
Currently, PagerWave has these drawbacks:
Backwards navigation is not supported.
Retrieving total item count is not supported.
Sorting by text might work, but is sketchy as the text would have to appear in the URL.
These may or may not be solved in the future.
Example
<?php
use PagerWave\Definition;
use PagerWave\EntryReader\SimpleEntryReader;
use PagerWave\QueryReader\NativeQueryReader;
use PagerWave\Extension\DoctrineOrm\QueryBuilderAdapter;
use PagerWave\Paginator;
use PagerWave\UrlGenerator\NativeUrlGenerator;
$paginator = new Paginator(
new SimpleEntryReader(),
new NativeQueryReader(),
new NativeUrlGenerator()
);
$queryBuilder = $em->createQueryBuilder()
->select('p')
->from(\App\Entity\Post::class, 'p');
$adapter = new QueryBuilderAdapter($queryBuilder);
$definition = new Definition(['rank' => 'DESC', 'id' => 'ASC']);
$cursor = $paginator->paginate($adapter, 100, $definition);
foreach ($cursor as $post) {
printf("Title: %s (rank: %d)\n", $post->getTitle(), $post->getRank());
}
if ($cursor->hasNextPage()) {
printf('<a href="%s">Show more</a>', $cursor->getNextPageUrl());
}
Supported paginatable collections
- Arrays
- Doctrine DBAL query builders
- Doctrine ORM query builders
- Doctrine collections
that implement the
Selectable
interface (e.g.ArrayCollection
,PersistentCollection
, and evenEntityRepository
). - Unions of any of the above.
Supported HTTP abstractions
- Array (query reader)
- PHP superglobals (query reader, URL generator)
- Symfony HttpFoundation
Acknowledgements
PagerWave is based on theory from Use the Index, Luke, and has taken inspiration from the Pagerfanta library.
Licence
This project is released under the Zlib licence.