Skip to content

Commit a737b94

Browse files
authored
Use default order from Sort in KeysetPaginator (#223)
1 parent 18c5d1a commit a737b94

File tree

5 files changed

+66
-17
lines changed

5 files changed

+66
-17
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@
5151
- Chg #219: Don't check correctness of current page in `PaginatorInterface::isOnLastPage()` method (@vjik)
5252
- Chg #219: Rename `PaginatorException` to `InvalidPageException` (@vjik)
5353
- Chg #211, #221: Change PHP constraint in `composer.json` to `8.1 - 8.4` (@vjik)
54+
- New #223: Add `Sort::getDefaultOrder()` method (@vjik)
55+
- Enh #223: `KeysetPaginator` now uses default order from `Sort` when no sort is set (@vjik)
5456

5557
## 1.0.1 January 25, 2023
5658

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"require-dev": {
3939
"maglnet/composer-require-checker": "^4.7.1",
4040
"phpunit/phpunit": "^10.5.46",
41-
"rector/rector": "^2.0.15",
41+
"rector/rector": "^2.0.18",
4242
"roave/infection-static-analysis-plugin": "^1.35",
4343
"spatie/phpunit-watcher": "^1.24",
4444
"vimeo/psalm": "^5.26.1 || ^6.10.3"

src/Paginator/KeysetPaginator.php

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Yiisoft\Data\Reader\SortableDataInterface;
2020

2121
use function array_reverse;
22+
use function array_slice;
2223
use function count;
2324
use function key;
2425
use function reset;
@@ -121,10 +122,7 @@ public function __construct(ReadableDataInterface $dataReader)
121122
throw new InvalidArgumentException('Limited data readers are not supported by keyset pagination.');
122123
}
123124

124-
$sort = $dataReader->getSort();
125-
$this->assertSort($sort);
126-
127-
$this->dataReader = $dataReader;
125+
$this->dataReader = $this->prepareSortInDataReader($dataReader, $dataReader->getSort());
128126
}
129127

130128
public function __clone()
@@ -255,10 +253,8 @@ public function isSortable(): bool
255253

256254
public function withSort(?Sort $sort): static
257255
{
258-
$this->assertSort($sort);
259-
260256
$new = clone $this;
261-
$new->dataReader = $this->dataReader->withSort($sort);
257+
$new->dataReader = $this->prepareSortInDataReader($this->dataReader, $sort);
262258
return $new;
263259
}
264260

@@ -436,14 +432,26 @@ private function getFieldAndSortingFromSort(Sort $sort): array
436432
];
437433
}
438434

439-
private function assertSort(?Sort $sort): void
435+
/**
436+
* @param FilterableDataInterface&LimitableDataInterface&ReadableDataInterface<TKey, TValue>&SortableDataInterface $dataReader
437+
* @return FilterableDataInterface&LimitableDataInterface&ReadableDataInterface<TKey, TValue>&SortableDataInterface
438+
*/
439+
private function prepareSortInDataReader(ReadableDataInterface $dataReader, ?Sort $sort): ReadableDataInterface
440440
{
441441
if ($sort === null) {
442442
throw new InvalidArgumentException('Data sorting should be configured to work with keyset pagination.');
443443
}
444444

445445
if (empty($sort->getOrder())) {
446-
throw new InvalidArgumentException('Data should be always sorted to work with keyset pagination.');
446+
$defaultOrder = $sort->getDefaultOrder();
447+
if (empty($defaultOrder)) {
448+
throw new InvalidArgumentException('Data should be always sorted to work with keyset pagination.');
449+
}
450+
$sort = $sort->withOrder(
451+
array_slice($defaultOrder, 0, 1, true)
452+
);
447453
}
454+
455+
return $dataReader->withSort($sort);
448456
}
449457
}

src/Reader/Sort.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ final class Sort
5959
private bool $withDefaultSorting = true;
6060

6161
/**
62-
* @var array Logical fields to order by in form of [name => direction].
62+
* @var array Logical fields to order by in form of `[name => direction]`.
6363
* @psalm-var TOrder
6464
*/
6565
private array $currentOrder = [];
@@ -321,4 +321,17 @@ public function hasFieldInConfig(string $name): bool
321321
{
322322
return isset($this->config[$name]);
323323
}
324+
325+
/**
326+
* Get a default order for logical fields.
327+
*
328+
* @return TOrder
329+
*/
330+
public function getDefaultOrder(): array
331+
{
332+
return array_map(
333+
static fn(array $item) => $item['default'],
334+
$this->config
335+
);
336+
}
324337
}

tests/Paginator/KeysetPaginatorTest.php

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,15 +166,41 @@ public function testThrowsExceptionForWithSortNull(): void
166166
(new KeysetPaginator($dataReader))->withSort(null);
167167
}
168168

169-
public function testThrowsExceptionWhenNotSorted(): void
169+
public function testDefaultOrderUsage(): void
170170
{
171-
$sort = Sort::only(['id', 'name']);
171+
$sort = Sort::only(['name', 'id']);
172172
$dataReader = (new IterableDataReader(self::getDataSet()))->withSort($sort);
173+
$paginator = new KeysetPaginator($dataReader);
173174

174-
$this->expectException(InvalidArgumentException::class);
175-
$this->expectExceptionMessage('Data should be always sorted to work with keyset pagination.');
175+
$result = $paginator->read();
176176

177-
new KeysetPaginator($dataReader);
177+
$this->assertSame(
178+
self::getDataSet([4, 3, 2, 0, 1]),
179+
array_values($this->iterableToArray($result)),
180+
);
181+
}
182+
183+
public function testDefaultOrderUsageInPrevious(): void
184+
{
185+
$sort = Sort::only(['name', 'id']);
186+
$data = [
187+
['id' => 2, 'name' => 'A'],
188+
['id' => 1, 'name' => 'A'],
189+
['id' => 3, 'name' => 'B'],
190+
];
191+
$dataReader = (new IterableDataReader($data))->withSort($sort);
192+
$paginator = (new KeysetPaginator($dataReader))
193+
->withToken(PageToken::previous('B'));
194+
195+
$result = $paginator->read();
196+
197+
$this->assertSame(
198+
[
199+
['id' => 2, 'name' => 'A'],
200+
['id' => 1, 'name' => 'A'],
201+
],
202+
array_values($this->iterableToArray($result)),
203+
);
178204
}
179205

180206
public function testThrowsExceptionForWithSortNotSorted(): void
@@ -185,7 +211,7 @@ public function testThrowsExceptionForWithSortNotSorted(): void
185211
$this->expectException(InvalidArgumentException::class);
186212
$this->expectExceptionMessage('Data should be always sorted to work with keyset pagination.');
187213

188-
(new KeysetPaginator($dataReader))->withSort(Sort::only(['id', 'name']));
214+
(new KeysetPaginator($dataReader))->withSort(Sort::only([]));
189215
}
190216

191217
public function testPageSizeCannotBeLessThanOne(): void

0 commit comments

Comments
 (0)