From 9110caabf46b0f56be889ed6dcdf4b0342af1a48 Mon Sep 17 00:00:00 2001 From: Paras Malhotra Date: Mon, 10 May 2021 19:26:36 +0530 Subject: [PATCH] Add cursor pagination support to API resources (#37315) --- .../Http/Resources/CollectsResources.php | 3 +- .../Resources/Json/ResourceCollection.php | 3 +- tests/Integration/Http/ResourceTest.php | 113 ++++++++++++++++++ 3 files changed, 117 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Http/Resources/CollectsResources.php b/src/Illuminate/Http/Resources/CollectsResources.php index 5c42da4225f5..00209dfc170d 100644 --- a/src/Illuminate/Http/Resources/CollectsResources.php +++ b/src/Illuminate/Http/Resources/CollectsResources.php @@ -2,6 +2,7 @@ namespace Illuminate\Http\Resources; +use Illuminate\Pagination\AbstractCursorPaginator; use Illuminate\Pagination\AbstractPaginator; use Illuminate\Support\Collection; use Illuminate\Support\Str; @@ -30,7 +31,7 @@ protected function collectResource($resource) ? $resource->mapInto($collects) : $resource->toBase(); - return $resource instanceof AbstractPaginator + return ($resource instanceof AbstractPaginator || $resource instanceof AbstractCursorPaginator) ? $resource->setCollection($this->collection) : $this->collection; } diff --git a/src/Illuminate/Http/Resources/Json/ResourceCollection.php b/src/Illuminate/Http/Resources/Json/ResourceCollection.php index 2931fd6463c7..aa7de80f45c4 100644 --- a/src/Illuminate/Http/Resources/Json/ResourceCollection.php +++ b/src/Illuminate/Http/Resources/Json/ResourceCollection.php @@ -4,6 +4,7 @@ use Countable; use Illuminate\Http\Resources\CollectsResources; +use Illuminate\Pagination\AbstractCursorPaginator; use Illuminate\Pagination\AbstractPaginator; use IteratorAggregate; @@ -108,7 +109,7 @@ public function toArray($request) */ public function toResponse($request) { - if ($this->resource instanceof AbstractPaginator) { + if ($this->resource instanceof AbstractPaginator || $this->resource instanceof AbstractCursorPaginator) { return $this->preparePaginatedResponse($request); } diff --git a/tests/Integration/Http/ResourceTest.php b/tests/Integration/Http/ResourceTest.php index a94f337299d1..47a852dc652d 100644 --- a/tests/Integration/Http/ResourceTest.php +++ b/tests/Integration/Http/ResourceTest.php @@ -6,6 +6,8 @@ use Illuminate\Http\Resources\Json\JsonResource; use Illuminate\Http\Resources\MergeValue; use Illuminate\Http\Resources\MissingValue; +use Illuminate\Pagination\Cursor; +use Illuminate\Pagination\CursorPaginator; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Route; @@ -678,6 +680,117 @@ public function testPaginatorResourceCanReceiveQueryParameters() ]); } + public function testCursorPaginatorReceiveLinks() + { + Route::get('/', function () { + $paginator = new CursorPaginator( + collect([new Post(['id' => 5, 'title' => 'Test Title']), new Post(['id' => 6, 'title' => 'Hello'])]), + 1, null, ['parameters' => ['id']] + ); + + return new PostCollectionResource($paginator); + }); + + $response = $this->withoutExceptionHandling()->get( + '/', ['Accept' => 'application/json'] + ); + + $response->assertStatus(200); + + $response->assertJson([ + 'data' => [ + [ + 'id' => 5, + 'title' => 'Test Title', + ], + ], + 'links' => [ + 'first' => null, + 'last' => null, + 'prev' => null, + 'next' => '/?cursor='.(new Cursor(['id' => 5]))->encode(), + ], + 'meta' => [ + 'path' => '/', + 'per_page' => 1, + ], + ]); + } + + public function testCursorPaginatorResourceCanPreserveQueryParameters() + { + Route::get('/', function () { + $collection = collect([new Post(['id' => 5, 'title' => 'Test Title']), new Post(['id' => 6, 'title' => 'Hello'])]); + $paginator = new CursorPaginator( + $collection, 1, null, ['parameters' => ['id']] + ); + + return PostCollectionResource::make($paginator)->preserveQuery(); + }); + + $response = $this->withoutExceptionHandling()->get( + '/?framework=laravel&author=Otwell', ['Accept' => 'application/json'] + ); + + $response->assertStatus(200); + + $response->assertJson([ + 'data' => [ + [ + 'id' => 5, + 'title' => 'Test Title', + ], + ], + 'links' => [ + 'first' => null, + 'last' => null, + 'prev' => null, + 'next' => '/?framework=laravel&author=Otwell&cursor='.(new Cursor(['id' => 5]))->encode(), + ], + 'meta' => [ + 'path' => '/', + 'per_page' => 1, + ], + ]); + } + + public function testCursorPaginatorResourceCanReceiveQueryParameters() + { + Route::get('/', function () { + $collection = collect([new Post(['id' => 5, 'title' => 'Test Title']), new Post(['id' => 6, 'title' => 'Hello'])]); + $paginator = new CursorPaginator( + $collection, 1, null, ['parameters' => ['id']] + ); + + return PostCollectionResource::make($paginator)->withQuery(['author' => 'Taylor']); + }); + + $response = $this->withoutExceptionHandling()->get( + '/?framework=laravel&author=Otwell', ['Accept' => 'application/json'] + ); + + $response->assertStatus(200); + + $response->assertJson([ + 'data' => [ + [ + 'id' => 5, + 'title' => 'Test Title', + ], + ], + 'links' => [ + 'first' => null, + 'last' => null, + 'prev' => null, + 'next' => '/?author=Taylor&cursor='.(new Cursor(['id' => 5]))->encode(), + ], + 'meta' => [ + 'path' => '/', + 'per_page' => 1, + ], + ]); + } + public function testToJsonMayBeLeftOffOfCollection() { Route::get('/', function () {