diff --git a/CHANGELOG.md b/CHANGELOG.md index 5aa2ab0..6b95a77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ * fix(`PagerItem`): `withPageNumber` should return static * build: update php-cs-fixer * feat(`QueryBuilderAdapter`): add `indexBy` parameter +* feat(`PageableInterface`): add `$start` parameter to `getPages()` method to + ease batch resuming. # 0.11.2 diff --git a/packages/rekapager-contracts/src/PageableInterface.php b/packages/rekapager-contracts/src/PageableInterface.php index e5e9f2a..0cdb366 100644 --- a/packages/rekapager-contracts/src/PageableInterface.php +++ b/packages/rekapager-contracts/src/PageableInterface.php @@ -56,9 +56,11 @@ public function getLastPage(): ?PageInterface; /** * Gets an iterable of all pages in the pageable object. * + * @param object|null $start The identifier of the starting page. If null, + * it will start from the first page. * @return \Traversable> */ - public function getPages(): \Traversable; + public function getPages(?object $start = null): \Traversable; /** * Gets the number of items per page. The actual items in the page may be diff --git a/packages/rekapager-contracts/src/Trait/TotalPagesTrait.php b/packages/rekapager-contracts/src/Trait/PageableTrait.php similarity index 53% rename from packages/rekapager-contracts/src/Trait/TotalPagesTrait.php rename to packages/rekapager-contracts/src/Trait/PageableTrait.php index c53ff29..34d8405 100644 --- a/packages/rekapager-contracts/src/Trait/TotalPagesTrait.php +++ b/packages/rekapager-contracts/src/Trait/PageableTrait.php @@ -13,8 +13,21 @@ namespace Rekalogika\Contracts\Rekapager\Trait; -trait TotalPagesTrait +use Rekalogika\Contracts\Rekapager\PageInterface; + +/** + * @template TKey of array-key + * @template-covariant T + */ +trait PageableTrait { + /** + * @return PageInterface + */ + abstract public function getFirstPage(): PageInterface; + + abstract public function getPageByIdentifier(object $pageIdentifier): PageInterface; + /** * @return int<1,max> */ @@ -41,4 +54,22 @@ public function getTotalPages(): ?int return $result; } + + /** + * @return \Traversable> + */ + public function getPages(?object $start = null): \Traversable + { + if ($start === null) { + $page = $this->getFirstPage(); + } else { + $page = $this->getPageByIdentifier($start); + } + + while ($page !== null) { + yield $page; + + $page = $page->getNextPage(); + } + } } diff --git a/packages/rekapager-keyset-pagination/src/KeysetPageable.php b/packages/rekapager-keyset-pagination/src/KeysetPageable.php index f2cecba..303ed4d 100644 --- a/packages/rekapager-keyset-pagination/src/KeysetPageable.php +++ b/packages/rekapager-keyset-pagination/src/KeysetPageable.php @@ -16,7 +16,7 @@ use Rekalogika\Contracts\Rekapager\Exception\InvalidArgumentException; use Rekalogika\Contracts\Rekapager\PageableInterface; use Rekalogika\Contracts\Rekapager\PageInterface; -use Rekalogika\Contracts\Rekapager\Trait\TotalPagesTrait; +use Rekalogika\Contracts\Rekapager\Trait\PageableTrait; use Rekalogika\Rekapager\Keyset\Contracts\BoundaryType; use Rekalogika\Rekapager\Keyset\Contracts\KeysetPageIdentifier; use Rekalogika\Rekapager\Keyset\Internal\KeysetPage; @@ -28,7 +28,10 @@ */ final class KeysetPageable implements PageableInterface { - use TotalPagesTrait; + /** + * @use PageableTrait + */ + use PageableTrait; /** * @var int<0,max>|null @@ -64,17 +67,6 @@ public function getItemsPerPage(): int return $this->itemsPerPage; } - public function getPages(): \Traversable - { - $page = $this->getFirstPage(); - - while ($page !== null) { - yield $page; - - $page = $page->getNextPage(); - } - } - /** * @return PageInterface */ diff --git a/packages/rekapager-offset-pagination/src/OffsetPageable.php b/packages/rekapager-offset-pagination/src/OffsetPageable.php index f463969..271d181 100644 --- a/packages/rekapager-offset-pagination/src/OffsetPageable.php +++ b/packages/rekapager-offset-pagination/src/OffsetPageable.php @@ -16,7 +16,7 @@ use Rekalogika\Contracts\Rekapager\Exception\InvalidArgumentException; use Rekalogika\Contracts\Rekapager\PageableInterface; use Rekalogika\Contracts\Rekapager\PageInterface; -use Rekalogika\Contracts\Rekapager\Trait\TotalPagesTrait; +use Rekalogika\Contracts\Rekapager\Trait\PageableTrait; use Rekalogika\Rekapager\Offset\Contracts\PageNumber; use Rekalogika\Rekapager\Offset\Internal\NullOffsetPage; use Rekalogika\Rekapager\Offset\Internal\OffsetPage; @@ -28,7 +28,10 @@ */ final class OffsetPageable implements PageableInterface { - use TotalPagesTrait; + /** + * @use PageableTrait + */ + use PageableTrait; /** * @var int<0,max>|null @@ -67,17 +70,6 @@ public function getItemsPerPage(): int return $this->itemsPerPage; } - public function getPages(): \Traversable - { - $page = $this->getFirstPage(); - - while ($page !== null) { - yield $page; - - $page = $page->getNextPage(); - } - } - public function getFirstPage(): PageInterface { return $this->getPageByIdentifier(new PageNumber(1)); diff --git a/packages/rekapager-pagerfanta-adapter/src/PagerfantaPageable.php b/packages/rekapager-pagerfanta-adapter/src/PagerfantaPageable.php index 9e5f274..050082a 100644 --- a/packages/rekapager-pagerfanta-adapter/src/PagerfantaPageable.php +++ b/packages/rekapager-pagerfanta-adapter/src/PagerfantaPageable.php @@ -57,9 +57,9 @@ public function getPageIdentifierClass(): string return $this->pageable->getPageIdentifierClass(); } - public function getPages(): \Traversable + public function getPages(?object $start = null): \Traversable { - return $this->pageable->getPages(); + return $this->pageable->getPages($start); } public function getFirstPage(): PageInterface diff --git a/tests/src/IntegrationTests/Pageable/BatchTest.php b/tests/src/IntegrationTests/Pageable/BatchTest.php index c172301..d505ca4 100644 --- a/tests/src/IntegrationTests/Pageable/BatchTest.php +++ b/tests/src/IntegrationTests/Pageable/BatchTest.php @@ -46,4 +46,46 @@ public function testBatch(string $pageableGeneratorClass): void static::assertSame(21, $pagesCount); static::assertCount(1003, $ids); } + + #[DataProviderExternal(PageableGeneratorProvider::class, 'all')] + public function testBatchResuming(string $pageableGeneratorClass): void + { + $pageable = $this->createPageableFromGenerator($pageableGeneratorClass); + + $itemsCount = 0; + $pagesCount = 0; + $ids = []; + + $currentIdentifier = null; + + foreach ($pageable->withItemsPerPage(50)->getPages() as $page) { + $currentIdentifier = $page->getPageIdentifier(); + + if ($pagesCount === 5) { + break; + } + + /** @var Post $post */ + foreach ($page as $post) { + $ids[$post->getId()] = true; + $itemsCount++; + } + $pagesCount++; + } + + foreach ($pageable->withItemsPerPage(50)->getPages($currentIdentifier) as $page) { + $currentIdentifier = $page->getPageIdentifier(); + + /** @var Post $post */ + foreach ($page as $post) { + $ids[$post->getId()] = true; + $itemsCount++; + } + $pagesCount++; + } + + static::assertSame(1003, $itemsCount); + static::assertSame(21, $pagesCount); + static::assertCount(1003, $ids); + } }