$ composer require germania-kg/pagination
Pagination: Page numbering made easy! Supports current, next, previous, first and last page numbers as well as number of pages and customizable page sizes.
PaginationFactory:
Callable class for creating a Pagination instance. Works great with $_GET['page']
PaginationIterator: Limits your Traversables according to the Pagination status. Useful for paginated JSON API resources.
JsonApiPaginationDecorator:
Create useful links
and meta
information for your JSON API resource collection.
Just pass the number of items to paginate. The Pagination class will calculate the page numbers, based on a default page size of 25. See below for customization examples.
<?hpp
use Germania\Pagination\Pagination;
$items_count = 100; // count( $things ) or integer;
$pagination = new Pagination( $items_count );
$pagination->getPagesCount(); // 4, with 25 items each
$pagination->getFirst(); // 0
$pagination->getLast(); // 3
Right after instantiation, no page was picked by the user. Hence, the Pagination is then considered inactive.
Please note that the current page also may be int(0)
– the first page. This is why we have to check against null
. The isActive method is a convenient alias for $p->getCurrent() === null
.
$pagination->isActive(); // FALSE
$pagination->getCurrent(); // null
$pagination->getPrevious(); // null
$pagination->getNext(); // null
It first needs a setCurrent call to become active:
use Germania\Pagination\PaginationRangeException;
try {
$pagination->setCurrent( 1 );
$pagination->isActive(); // TRUE
$pagination->getCurrent(); // 1
$pagination->getPrevious(); // null
$pagination->getNext(); // 2
}
catch ( PaginationRangeException $e )
{
echo $e->getMessage(); // "Invalid Page number"
echo $e->getCode(); // 400
}
Whilst the default number of items on a page is 25, you may set another size—up to 100 per default:
use Germania\Pagination\PaginationRangeException;
try {
$pagination = new Pagination( $items_count );
$pagination->getPageSize(); // 25
$pagination->setPageSize( 999 );
}
catch ( PaginationRangeException $e )
{
echo $e->getMessage(); // "Invalid Page size (max. 100)"
echo $e->getCode(); // 400
}
Tweak the page sizes with constructor parameters:
$custom_page_size = 50; // default: 25
$pagination = new Pagination( $items_count, $custom_page_size );
$max_page_size = 200; // default: 100
$pagination = new Pagination( $items_count, $custom_page_size, $max_page_size );
The PaginationFactory constructor also accepts instances of Countable
, Traversable
or arrays
, so you won't have to count the items yourself. The second parameter may be a page number integer or an array with number
and/or size
values.
<?php
use Germania\Pagination\PaginationFactory;
// Optinally set default page size
$factory = new PaginationFactory;
$factory = new PaginationFactory( 25 );
// Most simple: just integers
$items_count = 65;
$choose_page = 2;
// Create Pagination instance:
$pagination = $factory( $items_count, $choose_page );
Creation from array is useful when working with query parameters such as $_GET['page']
// Both elements are optional.
$pagination = $factory( $items_count, [
'number' => 2, // default: 0
'size' => 20
]);
// User Input
$pagination = $factory( $items_count, $_GET['page'] ?? [] );
Limits any \Traversable
iterator to the current page size, depending on the pagination status. The PaginationIterator constructor accepts your iterator and your pagination instance. It is also \Countable
to count the numbers of items shown on the current page.
<?php
use Germania\Pagination\PaginationIterator;
// Have your pagination at hand...
$pagination = ...
$pagination->setCurrent(2);
// Setup something really big
$collection = new MyHugeIterator( $thousand_items );
$paginated_collection = new PaginationIterator( $collection, $pagination );
echo count( $paginated_collection ); // 25
foreach( $paginated_collection as $item):
// loop: 25 items on page 2
endforeach;
Depending on the pagination status, PaginationIterator's inner iterator used in the foreach loop is either \LimitIterator
or the MyHugeIterator
instance itself, when pagination is not active:
// this time, we do not pick a page number!
$thousand_items = ...
$pagination = new Pagination( count($thousand_items) );
$pagination->isActive(); // null
$collection = new MyHugeIterator( $thousand_items );
$paginated_collection = new PaginationIterator( $collection, $pagination );
$iterator = $paginated_collection->getIterator();
get_class( $iterator ); // MyHugeIterator instance
This library provides a handy JsonApiPaginationDecorator which will generate useful information for your JSON API resource collection responses with the help of a given \Psr\Http\Message\UriInterface instance.
Meta information support
The meta
member can be used to include non-standard meta-information, such as our pagination:
$pagination->setCurrent( 16 );
$pagination->setPageSize( 10 );
$links = $ja_decorator->getMeta();
// array(
// numberOfPages => 23
// currentPage => 16
// pageSize => 10
// )
The result array will be empty, when the Pagination is inactive.
Links object support
The JSON API specs on fetching pagination proposes to use page[number]
and page[size]
for customizing the paged output. And it states the links
object in a collection must use these key names, when working with pagination links:
first
: the first page of datalast
: the last page of dataprev
: the previous page of datanext
: the next page of data
The JsonApiPaginationDecorator generates this elements for you. The class is \JsonSerializable
as well.
<?php
use Germania\Pagination\JsonApiPaginationDecorator;
// Prepare dependencies
$uri = \GuzzleHttp\Psr7\uri_for('http://example.com');
$pagination = ...
$pagination->setCurrent( 2 );
new $ja_decorator = new JsonApiPaginationDecorator( $pagination, $uri);
// These are equivalent:
$links = $ja_decorator->getLinks();
$links = $ja_decorator->jsonSerialize();
// array(
// first => http://example.com/?page[number]=0
// last => http://example.com/?page[number]=42
// previous => http://example.com/?page[number]=1
// next => http://example.com/?page[number]=3
// )
The JsonApiPaginationDecorator internally calls the withQuery method on the PSR-7 $uri instance. Unfortunately, this will replace any query parameters the URI contained. Just pass any needed query parameters as 3rd constructor parameter:
$params = array(
'foo' => 'bar',
'json' => 'cool'
);
new $ja_decorator = new JsonApiPaginationDecorator( $pagination, $uri, $params);
$links = $ja_decorator->getLinks();
// array(
// first => http://example.com/?page[number]=0&foo=bar&json=cool
// last => http://example.com/?page[number]=42&foo=bar&json=cool
// previous => http://example.com/?page[number]=1&foo=bar&json=cool
// next => http://example.com/?page[number]=3&foo=bar&json=cool
// )
In case you set a custom page size (which differs from the default size), the links will get an additional size
field:
$pagination->setPageSize( 10 );
$links = $ja_decorator->getLinks();
// array(
// first => http://example.com/?page[number]=0&page[size]=10
// last => http://example.com/?page[number]=42&page[size]=10
// previous => http://example.com/?page[number]=1&page[size]=10
// next => http://example.com/?page[number]=3&page[size]=10
// )
On the first and last page, links like previous
and next
do not make sense. Their value will be null:
$pagination = new Pagination (...);
new $ja_decorator = new JsonApiPaginationDecorator( $pagination, $uri);
$pagination->setCurrent( 0 );
$links = $ja_decorator->getLinks();
// array(
// ...
// previous => null
// next => http://example.com/?page[number]=1
// )
$pagination->setCurrent( 42 );
$links = $ja_decorator->getLinks();
// array(
// ...
// previous => http://example.com/?page[number]=41
// next => null
// )
When the pagination is not active, all values per default are null:
$pagination = new Pagination (...);
$pagination->isActive(); // FALSE
$ja_decorator->getLinks();
// array(
// first => null
// last => null
// previous => null
// next => null
// )
To get a clean, uncluttered links
array, you may pass a boolean filter flag as fourth constructor parameter:
$filter = true;
new $ja_decorator = new JsonApiPaginationDecorator( $pagination, $uri, [], $filter);
$ja_decorator->getLinks();
// array()
$ git clone https://github.com/GermaniaKG/Pagination.git
$ cd Pagination
$ composer install
Either copy phpunit.xml.dist
to phpunit.xml
and adapt to your needs, or leave as is. Run PhpUnit test or composer scripts like this:
$ composer test
# or
$ vendor/bin/phpunit