diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ba01879..2ebc88f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ - Bug #155: Fix `Sort` configuration preparation (@vjik) - Bug #155: Fix same named order fields in `Sort` were not overriding previous ones (@vjik) - New #158: Add methods `PaginatorInterface::isSortable()` and `PaginatorInterface::withSort()` (@vjik) +- Chg #159: Replace `withNextPageToken()` and `withPreviousPageToken()` of `PaginatorInterface` with `withToken()`, + `getNextPageToken()` with `getNextToken()`, `getPreviousPageToken()` with `getPreviousToken()`, and add `getToken()`. + These methods use new `PageToken` class (@vjik) ## 1.0.1 January 25, 2023 diff --git a/README.md b/README.md index 38eac5be..d5bfc58a 100644 --- a/README.md +++ b/README.md @@ -27,14 +27,14 @@ Features are: ## Requirements -- PHP 8.0 or higher. +- PHP 8.1 or higher. ## Installation The package could be installed with composer: ```shell -composer require yiisoft/data --prefer-dist +composer require yiisoft/data ``` ## Concepts @@ -334,7 +334,7 @@ $dataReader = (new MyDataReader(...)) $paginator = (new KeysetPaginator($dataReader)) ->withPageSize(10) - ->withNextPageToken('13'); + ->withToken(PageToken::next('13')); ``` When displaying first page ID (or another field name to paginate by) of the item displayed last is used with `withNextPageToken()` diff --git a/src/Paginator/KeysetPaginator.php b/src/Paginator/KeysetPaginator.php index 983ad66d..512d6c2c 100644 --- a/src/Paginator/KeysetPaginator.php +++ b/src/Paginator/KeysetPaginator.php @@ -61,8 +61,7 @@ final class KeysetPaginator implements PaginatorInterface * @var int Maximum number of items per page. */ private int $pageSize = self::DEFAULT_PAGE_SIZE; - private ?string $firstValue = null; - private ?string $lastValue = null; + private ?PageToken $token = null; private ?string $currentFirstValue = null; private ?string $currentLastValue = null; @@ -139,20 +138,16 @@ public function __clone() $this->currentLastValue = null; } - public function withNextPageToken(?string $token): static + public function withToken(?PageToken $token): static { $new = clone $this; - $new->firstValue = null; - $new->lastValue = $token; + $new->token = $token; return $new; } - public function withPreviousPageToken(?string $token): static + public function getToken(): ?PageToken { - $new = clone $this; - $new->firstValue = $token; - $new->lastValue = null; - return $new; + return $this->token; } public function withPageSize(int $pageSize): static @@ -201,19 +196,19 @@ public function read(): iterable /** @infection-ignore-all Any value more one in line below will be ignored into `readData()` method */ $dataReader = $this->dataReader->withLimit($this->pageSize + 1); - if ($this->isGoingToPreviousPage()) { + if ($this->token?->isPrevious === true) { $sort = $this->reverseSort($sort); $dataReader = $dataReader->withSort($sort); } - if ($this->isGoingSomewhere()) { + if ($this->token !== null) { $dataReader = $dataReader->withFilter($this->getFilter($sort)); $this->hasPreviousPage = $this->previousPageExist($dataReader, $sort); } $data = $this->readData($dataReader, $sort); - if ($this->isGoingToPreviousPage()) { + if ($this->token?->isPrevious === true) { $data = $this->reverseData($data); } @@ -240,14 +235,18 @@ public function getCurrentPageSize(): int return count($this->readCache); } - public function getPreviousPageToken(): ?string + public function getPreviousToken(): ?PageToken { - return $this->isOnFirstPage() ? null : $this->currentFirstValue; + return $this->isOnFirstPage() + ? null + : ($this->currentFirstValue === null ? null : PageToken::previous($this->currentFirstValue)); } - public function getNextPageToken(): ?string + public function getNextToken(): ?PageToken { - return $this->isOnLastPage() ? null : $this->currentLastValue; + return $this->isOnLastPage() + ? null + : ($this->currentLastValue === null ? null : PageToken::next($this->currentLastValue)); } public function isSortable(): bool @@ -269,7 +268,7 @@ public function getSort(): ?Sort public function isOnFirstPage(): bool { - if ($this->lastValue === null && $this->firstValue === null) { + if ($this->token === null) { return true; } @@ -354,7 +353,10 @@ private function previousPageExist(ReadableDataInterface $dataReader, Sort $sort private function getFilter(Sort $sort): FilterInterface { - $value = $this->getValue(); + /** + * @psalm-var PageToken $this->token The code calling this method must ensure that page token is not null. + */ + $value = $this->token->value; [$field, $sorting] = $this->getFieldAndSortingFromSort($sort); $filter = $sorting === SORT_ASC ? new GreaterThan($field, $value) : new LessThan($field, $value); @@ -375,7 +377,10 @@ private function getFilter(Sort $sort): FilterInterface private function getReverseFilter(Sort $sort): FilterInterface { - $value = $this->getValue(); + /** + * @psalm-var PageToken $this->token The code calling this method must ensure that page token is not null. + */ + $value = $this->token->value; [$field, $sorting] = $this->getFieldAndSortingFromSort($sort); $filter = $sorting === SORT_ASC ? new LessThanOrEqual($field, $value) : new GreaterThanOrEqual($field, $value); @@ -394,15 +399,6 @@ private function getReverseFilter(Sort $sort): FilterInterface ); } - /** - * @psalm-suppress NullableReturnStatement, InvalidNullableReturnType, PossiblyNullArgument The code calling this - * method must ensure that at least one of the properties `$firstValue` or `$lastValue` is not `null`. - */ - private function getValue(): string - { - return $this->isGoingToPreviousPage() ? $this->firstValue : $this->lastValue; - } - private function reverseSort(Sort $sort): Sort { $order = $sort->getOrder(); @@ -426,14 +422,4 @@ private function getFieldAndSortingFromSort(Sort $sort): array reset($order) === 'asc' ? SORT_ASC : SORT_DESC, ]; } - - private function isGoingToPreviousPage(): bool - { - return $this->firstValue !== null && $this->lastValue === null; - } - - private function isGoingSomewhere(): bool - { - return $this->firstValue !== null || $this->lastValue !== null; - } } diff --git a/src/Paginator/OffsetPaginator.php b/src/Paginator/OffsetPaginator.php index bfe6fb8c..97a595c0 100644 --- a/src/Paginator/OffsetPaginator.php +++ b/src/Paginator/OffsetPaginator.php @@ -40,9 +40,9 @@ final class OffsetPaginator implements PaginatorInterface { /** - * @var int Current page number. + * @var PageToken Current page token */ - private int $currentPage = 1; + private PageToken $token; /** * @var int Maximum number of items per page. @@ -85,16 +85,12 @@ public function __construct(ReadableDataInterface $dataReader) } $this->dataReader = $dataReader; + $this->token = PageToken::next('1'); } - public function withNextPageToken(?string $token): static + public function withToken(?PageToken $token): static { - return $this->withCurrentPage((int) $token); - } - - public function withPreviousPageToken(?string $token): static - { - return $this->withCurrentPage((int) $token); + return $this->withCurrentPage($token === null ? 1 : (int)$token->value); } public function withPageSize(int $pageSize): static @@ -124,18 +120,23 @@ public function withCurrentPage(int $page): self } $new = clone $this; - $new->currentPage = $page; + $new->token = PageToken::next((string) $page); return $new; } - public function getNextPageToken(): ?string + public function getToken(): PageToken { - return $this->isOnLastPage() ? null : (string) ($this->currentPage + 1); + return $this->token; } - public function getPreviousPageToken(): ?string + public function getNextToken(): ?PageToken { - return $this->isOnFirstPage() ? null : (string) ($this->currentPage - 1); + return $this->isOnLastPage() ? null : PageToken::next((string) ($this->getCurrentPage() + 1)); + } + + public function getPreviousToken(): ?PageToken + { + return $this->isOnFirstPage() ? null : PageToken::next((string) ($this->getCurrentPage() - 1)); } public function getPageSize(): int @@ -150,7 +151,7 @@ public function getPageSize(): int */ public function getCurrentPage(): int { - return $this->currentPage; + return (int) $this->token->value; } public function getCurrentPageSize(): int @@ -161,11 +162,13 @@ public function getCurrentPageSize(): int return $this->getTotalItems(); } - if ($this->currentPage < $pages) { + $currentPage = $this->getCurrentPage(); + + if ($currentPage < $pages) { return $this->pageSize; } - if ($this->currentPage === $pages) { + if ($currentPage === $pages) { return $this->getTotalItems() - $this->getOffset(); } @@ -179,7 +182,7 @@ public function getCurrentPageSize(): int */ public function getOffset(): int { - return $this->pageSize * ($this->currentPage - 1); + return $this->pageSize * ($this->getCurrentPage() - 1); } /** @@ -228,7 +231,7 @@ public function getSort(): ?Sort */ public function read(): iterable { - if ($this->currentPage > $this->getInternalTotalPages()) { + if ($this->getCurrentPage() > $this->getInternalTotalPages()) { throw new PaginatorException('Page not found.'); } @@ -248,16 +251,16 @@ public function readOne(): array|object|null public function isOnFirstPage(): bool { - return $this->currentPage === 1; + return $this->token->value === '1'; } public function isOnLastPage(): bool { - if ($this->currentPage > $this->getInternalTotalPages()) { + if ($this->getCurrentPage() > $this->getInternalTotalPages()) { throw new PaginatorException('Page not found.'); } - return $this->currentPage === $this->getInternalTotalPages(); + return $this->getCurrentPage() === $this->getInternalTotalPages(); } public function isPaginationRequired(): bool diff --git a/src/Paginator/PageToken.php b/src/Paginator/PageToken.php new file mode 100644 index 00000000..f1f3d770 --- /dev/null +++ b/src/Paginator/PageToken.php @@ -0,0 +1,24 @@ + 1, 'name' => 'Codename Boris', @@ -198,10 +201,7 @@ public function testEmptyData(): void { $sort = Sort::only(['id', 'name'])->withOrderString('id'); $dataReader = (new IterableDataReader([]))->withSort($sort); - $paginator = (new KeysetPaginator($dataReader)) - ->withPageSize(1) - ->withNextPageToken('1') - ; + $paginator = (new KeysetPaginator($dataReader))->withPageSize(1); $this->assertTrue($paginator->isOnFirstPage()); $this->assertTrue($paginator->isOnLastPage()); @@ -217,8 +217,7 @@ public function testReadFirstPage(): void $expected = self::getDataSet([0, 1]); $this->assertSame($expected, $this->iterableToArray($paginator->read())); - $last = end($expected); - $this->assertSame((string) $last['id'], $paginator->getNextPageToken()); + $this->assertPageToken((string) end($expected)['id'], false, $paginator->getNextToken()); $this->assertTrue($paginator->isOnFirstPage()); } @@ -235,7 +234,7 @@ public function testReadObjectsWithPublicProperties(): void $paginator = (new KeysetPaginator($dataReader))->withPageSize(2); $this->assertSame([$data[0], $data[1]], $this->iterableToArray($paginator->read())); - $this->assertSame((string) $data[1]->id, $paginator->getNextPageToken()); + $this->assertPageToken((string) $data[1]->id, false, $paginator->getNextToken()); $this->assertTrue($paginator->isOnFirstPage()); } @@ -261,7 +260,7 @@ public function testReadObjectsWithGetters(string $orderByField, string $getter) $paginator = (new KeysetPaginator($dataReader))->withPageSize(2); $this->assertSame([$data[0], $data[1]], $this->iterableToArray($paginator->read())); - $this->assertSame((string) $data[1]->$getter(), $paginator->getNextPageToken()); + $this->assertPageToken((string) $data[1]->$getter(), false, $paginator->getNextToken()); $this->assertTrue($paginator->isOnFirstPage()); } @@ -274,13 +273,12 @@ public function testReadSecondPage(): void $paginator = (new KeysetPaginator($dataReader)) ->withPageSize(2) - ->withNextPageToken('2'); + ->withToken(PageToken::next('2')); $expected = self::getDataSet([2, 3]); $this->assertSame($expected, array_values((array) $paginator->read())); - $last = end($expected); - $this->assertSame((string)$last['id'], $paginator->getNextPageToken()); + $this->assertPageToken((string) end($expected)['id'], false, $paginator->getNextToken()); } public function testReadSecondPageOrderedByName(): void @@ -290,14 +288,12 @@ public function testReadSecondPageOrderedByName(): void $paginator = (new KeysetPaginator($dataReader)) ->withPageSize(2) - ->withNextPageToken('Agent J') - ; + ->withToken(PageToken::next('Agent J')); $expected = self::getDataSet([2, 0]); $this->assertSame($expected, array_values($this->iterableToArray($paginator->read()))); - $last = end($expected); - $this->assertSame((string)$last['name'], $paginator->getNextPageToken()); + $this->assertPageToken((string) end($expected)['name'], false, $paginator->getNextToken()); } public static function dataReadOne(): array @@ -338,17 +334,25 @@ public function testBackwardPagination(): void $dataReader = (new IterableDataReader(self::getDataSet()))->withSort($sort); $paginator = (new KeysetPaginator($dataReader)) ->withPageSize(2) - ->withPreviousPageToken('5') + ->withToken(PageToken::previous('5')) ; $expected = self::getDataSet([1, 2]); $read = array_values($this->iterableToArray($paginator->read())); $this->assertSame($expected, $read); - $first = reset($expected); - $last = end($expected); - $this->assertSame((string) $last['id'], $paginator->getNextPageToken(), 'Last value fail!'); - $this->assertSame((string) $first['id'], $paginator->getPreviousPageToken(), 'First value fail!'); + $this->assertPageToken( + (string) end($expected)['id'], + false, + $paginator->getNextToken(), + 'Last value fail!', + ); + $this->assertPageToken( + (string) reset($expected)['id'], + true, + $paginator->getPreviousToken(), + 'First value fail!', + ); } public function testForwardAndBackwardPagination(): void @@ -357,31 +361,25 @@ public function testForwardAndBackwardPagination(): void $dataReader = (new IterableDataReader(self::getDataSet()))->withSort($sort); $paginator = (new KeysetPaginator($dataReader)) ->withPageSize(2) - ->withNextPageToken('2') - ; + ->withToken(PageToken::next('2')); $expected = self::getDataSet([2, 3]); $read = array_values($this->iterableToArray($paginator->read())); $this->assertSame($expected, $read); - - $first = reset($expected); - $last = end($expected); - - $this->assertSame((string) $last['id'], $paginator->getNextPageToken(), 'Last value fail!'); - $this->assertSame((string) $first['id'], $paginator->getPreviousPageToken(), 'First value fail!'); + $this->assertPageToken((string) end($expected)['id'], false, $paginator->getNextToken()); + $this->assertPageToken((string) reset($expected)['id'], true, $paginator->getPreviousToken()); $paginator = (new KeysetPaginator($dataReader)) ->withPageSize(2) - ->withPreviousPageToken($paginator->getPreviousPageToken()); + ->withToken(PageToken::previous($paginator->getPreviousToken()->value)); $expected = self::getDataSet([0, 1]); $read = array_values($this->iterableToArray($paginator->read())); $this->assertSame($expected, $read); - $last = end($expected); - $this->assertSame((string) $last['id'], $paginator->getNextPageToken(), 'Last value fail!'); - $this->assertNull($paginator->getPreviousPageToken(), 'First value fail!'); + $this->assertPageToken((string) end($expected)['id'], false, $paginator->getNextToken(), 'Last value fail!'); + $this->assertNull($paginator->getPreviousToken(), 'First value fail!'); } public function testIsOnFirstPage(): void @@ -400,19 +398,19 @@ public function testIsOnLastPage(): void $dataReader = (new IterableDataReader(self::getDataSet()))->withSort($sort); $paginator = (new KeysetPaginator($dataReader))->withPageSize(2); - $paginator = $paginator->withNextPageToken('6'); + $paginator = $paginator->withToken(PageToken::next('6')); $this->assertTrue($paginator->isOnLastPage()); - $paginator = $paginator->withNextPageToken('5'); + $paginator = $paginator->withToken(PageToken::next('5')); $this->assertTrue($paginator->isOnLastPage()); - $paginator = $paginator->withNextPageToken('4'); + $paginator = $paginator->withToken(PageToken::next('4')); $this->assertTrue($paginator->isOnLastPage()); - $paginator = $paginator->withNextPageToken('3'); + $paginator = $paginator->withToken(PageToken::next('3')); $this->assertTrue($paginator->isOnLastPage()); - $paginator = $paginator->withNextPageToken('2'); + $paginator = $paginator->withToken(PageToken::next('2')); $this->assertFalse($paginator->isOnLastPage()); $this->assertTrue($paginator->isPaginationRequired()); } @@ -437,22 +435,22 @@ public function testCurrentPageSize(): void $paginator = (new KeysetPaginator($dataReader))->withPageSize(2); $this->assertSame(2, $paginator->getCurrentPageSize()); - $paginator = $paginator->withPreviousPageToken('1'); + $paginator = $paginator->withToken(PageToken::previous('1')); $this->assertSame(0, $paginator->getCurrentPageSize()); - $paginator = $paginator->withPreviousPageToken('2'); + $paginator = $paginator->withToken(PageToken::previous('2')); $this->assertSame(1, $paginator->getCurrentPageSize()); - $paginator = $paginator->withPreviousPageToken('3'); + $paginator = $paginator->withToken(PageToken::previous('3')); $this->assertSame(2, $paginator->getCurrentPageSize()); - $paginator = $paginator->withNextPageToken('6'); + $paginator = $paginator->withToken(PageToken::next('6')); $this->assertSame(0, $paginator->getCurrentPageSize()); - $paginator = $paginator->withNextPageToken('5'); + $paginator = $paginator->withToken(PageToken::next('5')); $this->assertSame(1, $paginator->getCurrentPageSize()); - $paginator = $paginator->withNextPageToken('4'); + $paginator = $paginator->withToken(PageToken::next('4')); $this->assertSame(2, $paginator->getCurrentPageSize()); } @@ -509,45 +507,41 @@ public function testTokenResults(): void $sort = Sort::only(['id'])->withOrderString('id'); $dataReader = (new IterableDataReader(self::getDataSet()))->withSort($sort); $paginator = (new KeysetPaginator($dataReader))->withPageSize(2); - $this->assertNotNull($paginator->getNextPageToken()); + $this->assertNotNull($paginator->getNextToken()); - $paginator = $paginator->withPreviousPageToken('1'); + $paginator = $paginator->withToken(PageToken::previous('1')); try { - $paginator->getNextPageToken(); + $paginator->getNextToken(); $this->fail(); } catch (RuntimeException) { $this->assertTrue(true); } - $this->assertNull($paginator->getPreviousPageToken()); + $this->assertNull($paginator->getPreviousToken()); - $paginator = $paginator->withPreviousPageToken('2'); - $this->assertNotNull($paginator->getNextPageToken()); - $this->assertSame('1', $paginator->getNextPageToken()); - $this->assertNull($paginator->getPreviousPageToken()); + $paginator = $paginator->withToken(PageToken::previous('2')); + $this->assertPageToken('1', false, $paginator->getNextToken()); + $this->assertNull($paginator->getPreviousToken()); - $paginator = $paginator->withPreviousPageToken('3'); - $this->assertNotNull($paginator->getNextPageToken()); - $this->assertSame('2', $paginator->getNextPageToken()); - $this->assertNull($paginator->getPreviousPageToken()); + $paginator = $paginator->withToken(PageToken::previous('3')); + $this->assertPageToken('2', false, $paginator->getNextToken()); + $this->assertNull($paginator->getPreviousToken()); - $paginator = $paginator->withNextPageToken('6'); + $paginator = $paginator->withToken(PageToken::next('6')); try { - $paginator->getPreviousPageToken(); + $paginator->getPreviousToken(); $this->fail(); } catch (RuntimeException) { $this->assertTrue(true); } - $this->assertNull($paginator->getNextPageToken()); + $this->assertNull($paginator->getNextToken()); - $paginator = $paginator->withNextPageToken('5'); - $this->assertNotNull($paginator->getPreviousPageToken()); - $this->assertSame('6', $paginator->getPreviousPageToken()); - $this->assertNull($paginator->getNextPageToken()); + $paginator = $paginator->withToken(PageToken::next('5')); + $this->assertPageToken('6', true, $paginator->getPreviousToken()); + $this->assertNull($paginator->getNextToken()); - $paginator = $paginator->withNextPageToken('4'); - $this->assertNull($paginator->getNextPageToken()); - $this->assertNotNull($paginator->getPreviousPageToken()); - $this->assertSame('5', $paginator->getPreviousPageToken()); + $paginator = $paginator->withToken(PageToken::next('4')); + $this->assertNull($paginator->getNextToken()); + $this->assertPageToken('5', true, $paginator->getPreviousToken()); } public function testDefaultPageSize(): void @@ -694,7 +688,7 @@ public function testWithPreviousPageTokenAndIsOnFirstPageSameTrue(): void { $sort = Sort::only(['id'])->withOrderString('id'); $dataReader = (new IterableDataReader(self::getDataSet()))->withSort($sort); - $paginator = (new KeysetPaginator($dataReader))->withPreviousPageToken('1'); + $paginator = (new KeysetPaginator($dataReader))->withToken(PageToken::previous('1')); $this->assertTrue($paginator->isOnFirstPage()); } @@ -710,11 +704,11 @@ public function testCloneClearValues(): void $this->assertSame('1', $this->getInaccessibleProperty($paginator, 'currentFirstValue')); $this->assertSame('6', $this->getInaccessibleProperty($paginator, 'currentLastValue')); - $paginator = $paginator->withPreviousPageToken('1'); + $paginator = $paginator->withToken(PageToken::previous('1')); $paginator->read(); $this->assertTrue($this->getInaccessibleProperty($paginator, 'hasNextPage')); - $paginator = $paginator->withNextPageToken('3'); + $paginator = $paginator->withToken(PageToken::next('3')); $paginator->read(); $this->assertTrue($this->getInaccessibleProperty($paginator, 'hasPreviousPage')); @@ -733,8 +727,8 @@ public function testImmutability(): void $dataReader = (new IterableDataReader(self::getDataSet()))->withSort($sort); $paginator = new KeysetPaginator($dataReader); - $this->assertNotSame($paginator, $paginator->withNextPageToken('1')); - $this->assertNotSame($paginator, $paginator->withPreviousPageToken('1')); + $this->assertNotSame($paginator, $paginator->withToken(PageToken::next('1'))); + $this->assertNotSame($paginator, $paginator->withToken(PageToken::previous('1'))); $this->assertNotSame($paginator, $paginator->withPageSize(1)); $this->assertNotSame($paginator, $paginator->withFilterCallback(null)); } @@ -743,7 +737,7 @@ public function testGetPreviousPageExistForCoverage(): void { $sort = Sort::only(['id'])->withOrderString('id'); $dataReader = (new IterableDataReader(self::getDataSet()))->withSort($sort); - $paginator = (new KeysetPaginator($dataReader))->withNextPageToken('1'); + $paginator = (new KeysetPaginator($dataReader))->withToken(PageToken::next('1')); $this->assertTrue($this->invokeMethod($paginator, 'previousPageExist', [$dataReader, $sort])); @@ -753,7 +747,7 @@ public function testGetFilterForCoverage(): void { $sort = Sort::only(['id'])->withOrderString('id'); $dataReader = (new IterableDataReader([]))->withSort($sort); - $paginator = (new KeysetPaginator($dataReader))->withNextPageToken('1'); + $paginator = (new KeysetPaginator($dataReader))->withToken(PageToken::next('1')); $this->assertInstanceOf( GreaterThan::class, @@ -764,7 +758,7 @@ public function testGetFilterForCoverage(): void ->withOrderString('id') ->withOrder(['id' => 'desc']); $dataReader = (new IterableDataReader([]))->withSort($sort); - $paginator = (new KeysetPaginator($dataReader))->withNextPageToken('1'); + $paginator = (new KeysetPaginator($dataReader))->withToken(PageToken::next('1')); $this->assertInstanceOf( LessThan::class, @@ -776,7 +770,7 @@ public function testGetReverseFilterForCoverage(): void { $sort = Sort::only(['42'])->withOrderString('42'); $dataReader = (new IterableDataReader([]))->withSort($sort); - $paginator = (new KeysetPaginator($dataReader))->withNextPageToken('1'); + $paginator = (new KeysetPaginator($dataReader))->withToken(PageToken::next('1')); $this->assertInstanceOf( LessThanOrEqual::class, @@ -787,7 +781,7 @@ public function testGetReverseFilterForCoverage(): void ->withOrderString('42') ->withOrder(['42' => 'desc']); $dataReader = (new IterableDataReader([]))->withSort($sort); - $paginator = (new KeysetPaginator($dataReader))->withNextPageToken('1'); + $paginator = (new KeysetPaginator($dataReader))->withToken(PageToken::next('1')); $this->assertInstanceOf( GreaterThanOrEqual::class, @@ -806,7 +800,7 @@ static function ($item) { ))->withSort(Sort::only(['id'])->withOrderString('id')); $paginator = (new KeysetPaginator($dataReader)) ->withPageSize(2) - ->withPreviousPageToken('5') + ->withToken(PageToken::previous('5')) ->withFilterCallback( static function ( GreaterThan|LessThan|GreaterThanOrEqual|LessThanOrEqual $filter, @@ -845,7 +839,7 @@ static function ($item) { ))->withSort(Sort::only(['id'])->withOrderString('id')); $paginator = (new KeysetPaginator($dataReader)) ->withPageSize(2) - ->withPreviousPageToken('2') + ->withToken(PageToken::previous('2')) ->withFilterCallback( static function ( GreaterThan|LessThan|GreaterThanOrEqual|LessThanOrEqual $filter, @@ -889,7 +883,7 @@ public function testFilterCallbackWithReverse(): void $dataReader = (new IterableDataReader(self::DEFAULT_DATASET)) ->withSort(Sort::only(['id'])->withOrderString('id')); $paginator = (new KeysetPaginator($dataReader)) - ->withPreviousPageToken('1') + ->withToken(PageToken::previous('1')) ->withFilterCallback( static function ( GreaterThan|LessThan|GreaterThanOrEqual|LessThanOrEqual $filter, @@ -966,7 +960,7 @@ public function testPageTypeWithPreviousPageToken( $paginator = (new KeysetPaginator($reader)) ->withPageSize(2) - ->withPreviousPageToken($token); + ->withToken(PageToken::previous($token)); $this->assertSame($expectedIsOnFirstPage, $paginator->isOnFirstPage()); $this->assertSame($expectedIsOnLastPage, $paginator->isOnLastPage()); @@ -1029,7 +1023,7 @@ public function testPageTypeWithNextPageToken( $paginator = (new KeysetPaginator($reader)) ->withPageSize(2) - ->withNextPageToken($token); + ->withToken(PageToken::next($token)); $this->assertSame($expectedIsOnFirstPage, $paginator->isOnFirstPage()); $this->assertSame($expectedIsOnLastPage, $paginator->isOnLastPage()); @@ -1045,7 +1039,7 @@ public function testIsSortable(): void $this->assertTrue($paginator->isSortable()); } - public function testWithSort() + public function testWithSort(): void { $sort1 = Sort::only(['id'])->withOrderString('id'); $reader = (new IterableDataReader([]))->withSort($sort1); @@ -1058,4 +1052,18 @@ public function testWithSort() $this->assertSame($sort1, $paginator1->getSort()); $this->assertSame($sort2, $paginator2->getSort()); } + + public function testGetPageToken(): void + { + $sort = Sort::only(['id'])->withOrderString('id'); + $reader = (new IterableDataReader([]))->withSort($sort); + $paginator = new KeysetPaginator($reader); + + $this->assertNull($paginator->getToken()); + + $token = PageToken::next('1'); + $paginator = $paginator->withToken($token); + + $this->assertSame($token, $paginator->getToken()); + } } diff --git a/tests/Paginator/OffsetPaginatorTest.php b/tests/Paginator/OffsetPaginatorTest.php index 4a2512c1..24a47f8c 100644 --- a/tests/Paginator/OffsetPaginatorTest.php +++ b/tests/Paginator/OffsetPaginatorTest.php @@ -8,6 +8,7 @@ use LogicException; use PHPUnit\Framework\Attributes\DataProvider; use Yiisoft\Data\Paginator\OffsetPaginator; +use Yiisoft\Data\Paginator\PageToken; use Yiisoft\Data\Paginator\PaginatorException; use Yiisoft\Data\Paginator\PaginatorInterface; use Yiisoft\Data\Reader\CountableDataInterface; @@ -21,6 +22,8 @@ final class OffsetPaginatorTest extends TestCase { + use PageTokenAssertTrait; + private const ITEM_1 = [ 'id' => 1, 'name' => 'Codename Boris', @@ -486,33 +489,32 @@ public function testGetSort(): void public function testNextPageToken(): void { $dataReader = new IterableDataReader(self::DEFAULT_DATASET); - $paginator = (new OffsetPaginator($dataReader))->withNextPageToken('1'); + $paginator = (new OffsetPaginator($dataReader))->withToken(PageToken::next('1')); - $this->assertNull($paginator->getNextPageToken()); + $this->assertNull($paginator->getNextToken()); $paginator = $paginator->withPageSize(2); - $this->assertSame('2', $paginator->getNextPageToken()); + $this->assertPageToken('2', false, $paginator->getNextToken()); } public function testPreviousPageToken(): void { $dataReader = new IterableDataReader(self::DEFAULT_DATASET); - $paginator = (new OffsetPaginator($dataReader))->withPreviousPageToken('1'); + $paginator = (new OffsetPaginator($dataReader))->withToken(PageToken::previous('1')); - $this->assertNull($paginator->getPreviousPageToken()); + $this->assertNull($paginator->getPreviousToken()); - $paginator = $paginator->withPreviousPageToken('5'); + $paginator = $paginator->withToken(PageToken::previous('5')); - $this->assertSame('4', $paginator->getPreviousPageToken()); + $this->assertPageToken('4', false, $paginator->getPreviousToken()); } public function testImmutability(): void { $paginator = new OffsetPaginator(new IterableDataReader([])); - $this->assertNotSame($paginator, $paginator->withNextPageToken('1')); - $this->assertNotSame($paginator, $paginator->withPreviousPageToken('1')); + $this->assertNotSame($paginator, $paginator->withToken(PageToken::previous('1'))); $this->assertNotSame($paginator, $paginator->withPageSize(1)); $this->assertNotSame($paginator, $paginator->withCurrentPage(1)); } @@ -555,4 +557,15 @@ public function testWithSortNonSortableData(): void $this->expectExceptionMessage('Data reader does not support sorting.'); $paginator->withSort(null); } + + public function testWithNulledPageToken(): void + { + $paginator = (new OffsetPaginator(new StubOffsetData()))->withToken(null); + + $token = $paginator->getToken(); + + $this->assertNotNull($token); + $this->assertSame('1', $token->value); + $this->assertFalse($token->isPrevious); + } } diff --git a/tests/Paginator/PageTokenAssertTrait.php b/tests/Paginator/PageTokenAssertTrait.php new file mode 100644 index 00000000..7f395a1d --- /dev/null +++ b/tests/Paginator/PageTokenAssertTrait.php @@ -0,0 +1,27 @@ +value, new IsIdentical($expectedValue), $message); + static::assertThat($pageToken->isPrevious, new IsIdentical($expectedIsPrevious), $message); + } +}