diff --git a/CHANGELOG.md b/CHANGELOG.md index c0b01b8a..6ba01879 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Chg #154: Raise minimum required PHP version to 8.1 (@vjik) - 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) ## 1.0.1 January 25, 2023 diff --git a/src/Paginator/KeysetPaginator.php b/src/Paginator/KeysetPaginator.php index e34c8ecf..983ad66d 100644 --- a/src/Paginator/KeysetPaginator.php +++ b/src/Paginator/KeysetPaginator.php @@ -250,9 +250,20 @@ public function getNextPageToken(): ?string return $this->isOnLastPage() ? null : $this->currentLastValue; } + public function isSortable(): bool + { + return true; + } + + public function withSort(?Sort $sort): static + { + $new = clone $this; + $new->dataReader = $this->dataReader->withSort($sort); + return $new; + } + public function getSort(): ?Sort { - /** @psalm-var SortableDataInterface $this->dataReader */ return $this->dataReader->getSort(); } diff --git a/src/Paginator/OffsetPaginator.php b/src/Paginator/OffsetPaginator.php index 532bfde2..bfe6fb8c 100644 --- a/src/Paginator/OffsetPaginator.php +++ b/src/Paginator/OffsetPaginator.php @@ -6,6 +6,7 @@ use Generator; use InvalidArgumentException; +use LogicException; use Yiisoft\Data\Reader\CountableDataInterface; use Yiisoft\Data\Reader\LimitableDataInterface; use Yiisoft\Data\Reader\OffsetableDataInterface; @@ -201,6 +202,22 @@ public function getTotalPages(): int return (int) ceil($this->getTotalItems() / $this->pageSize); } + public function isSortable(): bool + { + return $this->dataReader instanceof SortableDataInterface; + } + + public function withSort(?Sort $sort): static + { + if (!$this->dataReader instanceof SortableDataInterface) { + throw new LogicException('Data reader does not support sorting.'); + } + + $new = clone $this; + $new->dataReader = $this->dataReader->withSort($sort); + return $new; + } + public function getSort(): ?Sort { return $this->dataReader instanceof SortableDataInterface ? $this->dataReader->getSort() : null; diff --git a/src/Paginator/PaginatorInterface.php b/src/Paginator/PaginatorInterface.php index b38ac3d4..3d728c5b 100644 --- a/src/Paginator/PaginatorInterface.php +++ b/src/Paginator/PaginatorInterface.php @@ -92,6 +92,22 @@ public function getPageSize(): int; */ public function getCurrentPageSize(): int; + /** + * @return bool Whether sorting is supported. + */ + public function isSortable(): bool; + + /** + * Get a new instance with sorting set. + * + * @param Sort|null $sort Sorting criteria or null for no sorting. + * + * @return static New instance. + * + * @throw LogicException When sorting is not supported. + */ + public function withSort(?Sort $sort): static; + /** * Get current sort object. * diff --git a/tests/Paginator/KeysetPaginatorTest.php b/tests/Paginator/KeysetPaginatorTest.php index 1b34f86a..4e3e96ba 100644 --- a/tests/Paginator/KeysetPaginatorTest.php +++ b/tests/Paginator/KeysetPaginatorTest.php @@ -1035,4 +1035,27 @@ public function testPageTypeWithNextPageToken( $this->assertSame($expectedIsOnLastPage, $paginator->isOnLastPage()); $this->assertSame($expectedIds, ArrayHelper::getColumn($paginator->read(), 'id', keepKeys: false)); } + + public function testIsSortable(): void + { + $sort = Sort::only(['id'])->withOrderString('id'); + $reader = (new IterableDataReader([]))->withSort($sort); + $paginator = new KeysetPaginator($reader); + + $this->assertTrue($paginator->isSortable()); + } + + public function testWithSort() + { + $sort1 = Sort::only(['id'])->withOrderString('id'); + $reader = (new IterableDataReader([]))->withSort($sort1); + $paginator1 = new KeysetPaginator($reader); + + $sort2 = $sort1->withOrderString('-id'); + $paginator2 = $paginator1->withSort($sort2); + + $this->assertNotSame($paginator1, $paginator2); + $this->assertSame($sort1, $paginator1->getSort()); + $this->assertSame($sort2, $paginator2->getSort()); + } } diff --git a/tests/Paginator/OffsetPaginatorTest.php b/tests/Paginator/OffsetPaginatorTest.php index b615494f..4a2512c1 100644 --- a/tests/Paginator/OffsetPaginatorTest.php +++ b/tests/Paginator/OffsetPaginatorTest.php @@ -5,6 +5,7 @@ namespace Yiisoft\Data\Tests\Paginator; use InvalidArgumentException; +use LogicException; use PHPUnit\Framework\Attributes\DataProvider; use Yiisoft\Data\Paginator\OffsetPaginator; use Yiisoft\Data\Paginator\PaginatorException; @@ -15,6 +16,7 @@ use Yiisoft\Data\Reader\OffsetableDataInterface; use Yiisoft\Data\Reader\ReadableDataInterface; use Yiisoft\Data\Reader\Sort; +use Yiisoft\Data\Tests\Support\StubOffsetData; use Yiisoft\Data\Tests\TestCase; final class OffsetPaginatorTest extends TestCase @@ -514,4 +516,43 @@ public function testImmutability(): void $this->assertNotSame($paginator, $paginator->withPageSize(1)); $this->assertNotSame($paginator, $paginator->withCurrentPage(1)); } + + public static function dataIsSupportSorting(): array + { + return [ + [true, new IterableDataReader([])], + [false, new StubOffsetData()], + ]; + } + + #[DataProvider('dataIsSupportSorting')] + public function testIsSortable(bool $expected, ReadableDataInterface $reader): void + { + $paginator = new OffsetPaginator($reader); + + $this->assertSame($expected, $paginator->isSortable()); + } + + public function testWithSort(): void + { + $sort1 = Sort::only(['id'])->withOrderString('id'); + $reader = (new IterableDataReader([]))->withSort($sort1); + $paginator1 = new OffsetPaginator($reader); + + $sort2 = $sort1->withOrderString('-id'); + $paginator2 = $paginator1->withSort($sort2); + + $this->assertNotSame($paginator1, $paginator2); + $this->assertSame($sort1, $paginator1->getSort()); + $this->assertSame($sort2, $paginator2->getSort()); + } + + public function testWithSortNonSortableData(): void + { + $paginator = new OffsetPaginator(new StubOffsetData()); + + $this->expectException(LogicException::class); + $this->expectExceptionMessage('Data reader does not support sorting.'); + $paginator->withSort(null); + } } diff --git a/tests/Support/StubOffsetData.php b/tests/Support/StubOffsetData.php new file mode 100644 index 00000000..2162e57e --- /dev/null +++ b/tests/Support/StubOffsetData.php @@ -0,0 +1,42 @@ +