Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ composer.phar

# phpunit itself is not needed
phpunit.phar
# psalm itself is not needed
psalm.phar
# local phpunit config
/phpunit.xml
# phpunit cache
Expand Down
17 changes: 17 additions & 0 deletions src/Paginator/KeysetPaginator.php
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,23 @@ public function getNextToken(): ?PageToken
: ($this->currentLastValue === null ? null : PageToken::next($this->currentLastValue));
}

/**
* Get a data reader for the next page.
*
* @return ReadableDataInterface|null Data reader for the next page or null if on last page.
* @psalm-return ReadableDataInterface<TKey, TValue>|null
*/
public function nextPage(): ?ReadableDataInterface
{
$nextToken = $this->getNextToken();
if ($nextToken === null) {
return null;
}

/** @var ReadableDataInterface<TKey, TValue> */
return $this->withToken($nextToken);
}

public function isSortable(): bool
{
return true;
Expand Down
17 changes: 17 additions & 0 deletions src/Paginator/OffsetPaginator.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,23 @@ public function getPreviousToken(): ?PageToken
return $this->isOnFirstPage() ? null : PageToken::next((string) ($this->getCurrentPage() - 1));
}

/**
* Get a data reader for the next page.
*
* @return ReadableDataInterface|null Data reader for the next page or `null` if on last page.
* @psalm-return ReadableDataInterface<TKey, TValue>|null
*/
public function nextPage(): ?ReadableDataInterface
{
$nextToken = $this->getNextToken();
if ($nextToken === null) {
return null;
}

/** @var ReadableDataInterface<TKey, TValue> */
return $this->withToken($nextToken);
}

public function getPageSize(): int
{
return $this->pageSize;
Expand Down
8 changes: 8 additions & 0 deletions src/Paginator/PaginatorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ public function getNextToken(): ?PageToken;
*/
public function getPreviousToken(): ?PageToken;

/**
* Get a data reader for the next page.
*
* @return ReadableDataInterface|null Data reader for the next page or `null` if on last page.
* @psalm-return ReadableDataInterface<TKey, TValue>|null
*/
public function nextPage(): ?ReadableDataInterface;

/**
* Get the maximum number of items per page.
*
Expand Down
53 changes: 53 additions & 0 deletions tests/Paginator/KeysetPaginatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1168,4 +1168,57 @@ public function testGetPageToken(): void

$this->assertSame($token, $paginator->getToken());
}

public function testNextPage(): void
{
$sort = Sort::only(['id', 'name'])->withOrderString('id');
$dataReader = (new IterableDataReader(self::getDataSet()))->withSort($sort);
$paginator = (new KeysetPaginator($dataReader))->withPageSize(2);

// Test first page has next page
$nextPageReader = $paginator->nextPage();
$this->assertInstanceOf(KeysetPaginator::class, $nextPageReader);

// Verify the next page returns correct data
$nextPageData = array_values($this->iterableToArray($nextPageReader->read()));
$expectedNextPageData = self::getDataSet([2, 3]);
$this->assertSame($expectedNextPageData, $nextPageData);

// Test that the returned page reader has correct token
$this->assertPageToken('2', false, $nextPageReader->getToken());
}

public function testNextPageReturnsNullOnLastPage(): void
{
$sort = Sort::only(['id', 'name'])->withOrderString('id');
$dataReader = (new IterableDataReader(self::getDataSet()))->withSort($sort);

// Create paginator that starts on the last page
$paginator = (new KeysetPaginator($dataReader))
->withPageSize(2)
->withToken(PageToken::next('4')); // This should be the last page

$nextPageReader = $paginator->nextPage();
$this->assertNull($nextPageReader);
}

public function testNextPageIterativeReading(): void
{
$sort = Sort::only(['id', 'name'])->withOrderString('id');
$dataReader = (new IterableDataReader(self::getDataSet()))->withSort($sort);
$paginator = (new KeysetPaginator($dataReader))->withPageSize(2);

$allData = [];
$currentPaginator = $paginator;

// Read all pages iteratively
while ($currentPaginator !== null) {
$pageData = array_values($this->iterableToArray($currentPaginator->read()));
$allData = array_merge($allData, $pageData);
$currentPaginator = $currentPaginator->nextPage();
}

// Verify we got all the data
$this->assertSame(self::getDataSet(), $allData);
}
}
50 changes: 50 additions & 0 deletions tests/Paginator/OffsetPaginatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -671,4 +671,54 @@ public function testReadOneWithLimit1(): void

$this->assertSame(self::ITEM_1, $result);
}

public function testNextPage(): void
{
$dataReader = new IterableDataReader(self::DEFAULT_DATASET);
$paginator = (new OffsetPaginator($dataReader))->withPageSize(2);

// Test first page has next page
$nextPageReader = $paginator->nextPage();
$this->assertInstanceOf(OffsetPaginator::class, $nextPageReader);

// Verify the next page returns correct data
$nextPageData = array_values($this->iterableToArray($nextPageReader->read()));
$expectedNextPageData = [self::ITEM_3, self::ITEM_4];
$this->assertSame($expectedNextPageData, $nextPageData);

// Test that the returned page reader has correct token
$this->assertPageToken('2', false, $nextPageReader->getToken());
}

public function testNextPageReturnsNullOnLastPage(): void
{
$dataReader = new IterableDataReader(self::DEFAULT_DATASET);

// Create paginator that starts on the last page
$paginator = (new OffsetPaginator($dataReader))
->withPageSize(2)
->withToken(PageToken::next('3')); // This should be the last page

$nextPageReader = $paginator->nextPage();
$this->assertNull($nextPageReader);
}

public function testNextPageIterativeReading(): void
{
$dataReader = new IterableDataReader(self::DEFAULT_DATASET);
$paginator = (new OffsetPaginator($dataReader))->withPageSize(2);

$allData = [];
$currentPaginator = $paginator;

// Read all pages iteratively
while ($currentPaginator !== null) {
$pageData = array_values($this->iterableToArray($currentPaginator->read()));
$allData = array_merge($allData, $pageData);
$currentPaginator = $currentPaginator->nextPage();
}

// Verify we got all the data
$this->assertSame(self::DEFAULT_DATASET, $allData);
}
}
Loading