From 30f153ffd07e222e4f9fd60fe1015f541592a3b6 Mon Sep 17 00:00:00 2001 From: Greg Haddow Date: Thu, 31 Oct 2024 17:59:31 +0000 Subject: [PATCH] fix: handle key column not present in pagination cursor (#41) * fix: keyName column may not be present in pagination cursor * test: add test to cover with Id Encoding combined with withoutKeySort behaviour --------- Co-authored-by: Gregory Haddow --- src/Pagination/Cursor/CursorParser.php | 7 +-- .../Pagination/CursorPaginationTest.php | 51 +++++++++++++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/Pagination/Cursor/CursorParser.php b/src/Pagination/Cursor/CursorParser.php index 5c17d0b..c91bbae 100644 --- a/src/Pagination/Cursor/CursorParser.php +++ b/src/Pagination/Cursor/CursorParser.php @@ -32,12 +32,13 @@ public function __construct(private IdParser $idParser, private string $keyName) */ public function encode(LaravelCursor $cursor): string { - $key = $cursor->parameter($this->keyName); - - if ($key) { + try { + $key = $cursor->parameter($this->keyName); $parameters = $this->withoutPrivate($cursor->toArray()); $parameters[$this->keyName] = $this->idParser->encode($key); $cursor = new LaravelCursor($parameters, $cursor->pointsToNextItems()); + } catch (\UnexpectedValueException $ex) { + // Do nothing as the cursor does not contain the key. } return $cursor->encode(); diff --git a/tests/lib/Acceptance/Pagination/CursorPaginationTest.php b/tests/lib/Acceptance/Pagination/CursorPaginationTest.php index 24776fc..429e06e 100644 --- a/tests/lib/Acceptance/Pagination/CursorPaginationTest.php +++ b/tests/lib/Acceptance/Pagination/CursorPaginationTest.php @@ -275,6 +275,57 @@ public function testWithoutCursor(): void $this->assertPage($posts->reverse()->take(3), $page); } + /** + * @return void + */ + public function testWithIdEncodingWithoutKeySort(): void + { + $this->withIdEncoding(); + $this->paginator->withoutKeySort(); + + $posts = Post::factory()->count(4)->sequence(['title' => 'd'], ['title' => 'c'], ['title' => 'b'], ['title' => 'a'])->create(); + + $meta = [ + 'from' => $this->encodeCursor( + ["posts.title"=> "a"], + pointsToNextItems: false, + ), + 'hasMore' => true, + 'perPage' => 3, + 'to' => $this->encodeCursor( + ["posts.title"=> "c"], + pointsToNextItems: true, + ), + ]; + + $links = [ + 'first' => [ + 'href' => 'http://localhost/api/v1/posts?' . Arr::query([ + 'page' => ['limit' => '3'], + 'sort' => 'title', + ]), + ], + 'next' => [ + 'href' => 'http://localhost/api/v1/posts?' . Arr::query([ + 'page' => [ + 'after' => $this->encodeCursor( + ["posts.title"=> "c"], + pointsToNextItems: true, + ), + 'limit' => '3', + ], + 'sort' => 'title', + ]), + ], + ]; + + $page = $this->posts->repository()->queryAll()->sort('title')->paginate(['limit' => '3']); + + $this->assertSame(['page' => $meta], $page->meta()); + $this->assertSame($links, $page->links()->toArray()); + $this->assertPage($posts->reverse()->take(3), $page); + } + /** * @return void */