Skip to content

Commit

Permalink
Support paginations aware of their total items
Browse files Browse the repository at this point in the history
  • Loading branch information
cerbero90 committed Jan 23, 2024
1 parent d54561d commit e762f75
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 9 deletions.
8 changes: 0 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,6 @@ LazyJsonPages::from($source)->lastPage('X-Last-Page');

APIs can expose their length information in the form of numbers (`total_pages: 10`) or URIs (`last_page: "https://example.com?page=10"`), Lazy JSON Pages supports both.

To save more memory when setting the total number of items, we can also define the number of items shown in each page:

```php
LazyJsonPages::from($source)
->totalItems('pagination.total_items')
->perPage(20);
```

When dealing with a lot of data, it may be a good idea to fetch only 1 item on the first page and leverage the length information on that page to calculate the total number of pages/items without having to load all the other items of that page.

We can do that by calling `perPage()` with:
Expand Down
2 changes: 1 addition & 1 deletion src/Paginations/AnyPagination.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class AnyPagination extends Pagination
// LimitPagination::class,
// LinkHeaderPagination::class,
// OffsetPagination::class,
// TotalItemsAwarePagination::class,
TotalItemsAwarePagination::class,
TotalPagesAwarePagination::class,
];

Expand Down
48 changes: 48 additions & 0 deletions src/Paginations/TotalItemsAwarePagination.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

declare(strict_types=1);

namespace Cerbero\LazyJsonPages\Paginations;

use Cerbero\LazyJsonPages\Exceptions\InvalidKeyException;
use Traversable;

/**
* The pagination aware of the total number of items.
*/
class TotalItemsAwarePagination extends LengthAwarePagination
{
/**
* Determine whether the configuration matches this pagination.
*/
public function matches(): bool
{
return $this->config->totalItemsKey !== null
&& $this->config->totalPagesKey === null
&& $this->config->perPage === null;
}

/**
* Yield the paginated items.
*
* @return Traversable<int, mixed>
*/
public function getIterator(): Traversable
{
$perPage = 0;
$generator = $this->yieldItemsAndReturnKey($this->source->response(), $this->config->totalItemsKey);

foreach ($generator as $item) {
yield $item;
++$perPage;
}

if (!is_numeric($totalItems = $generator->getReturn())) {
throw new InvalidKeyException($this->config->totalItemsKey);
}

$totalPages = $perPage > 0 ? (int) ceil(intval($totalItems) / $perPage) : 0;

yield from $this->yieldItemsUntilPage($totalPages);
}
}
29 changes: 29 additions & 0 deletions tests/Feature/PaginationTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

use Cerbero\LazyJsonPages\LazyJsonPages;

it('supports paginations aware of their total pages', function () {
$expectedItems = require fixture('items.php');
$lazyCollection = LazyJsonPages::from('https://example.com/api/v1/users')
->totalPages('meta.total_pages')
->collect('data.*');

expect($lazyCollection)->toLoadItemsViaRequests($expectedItems, [
'https://example.com/api/v1/users' => 'lengthAware/page1.json',
'https://example.com/api/v1/users?page=2' => 'lengthAware/page2.json',
'https://example.com/api/v1/users?page=3' => 'lengthAware/page3.json',
]);
});

it('supports paginations aware of their total items', function () {
$expectedItems = require fixture('items.php');
$lazyCollection = LazyJsonPages::from('https://example.com/api/v1/users')
->totalItems('meta.total_items')
->collect('data.*');

expect($lazyCollection)->toLoadItemsViaRequests($expectedItems, [
'https://example.com/api/v1/users' => 'lengthAware/page1.json',
'https://example.com/api/v1/users?page=2' => 'lengthAware/page2.json',
'https://example.com/api/v1/users?page=3' => 'lengthAware/page3.json',
]);
});

0 comments on commit e762f75

Please sign in to comment.