From 0ecd1b7cea1f48452e6f994dbd8f06f594721c9f Mon Sep 17 00:00:00 2001 From: Ruben Van Assche Date: Fri, 3 May 2024 11:38:07 +0200 Subject: [PATCH] Fix pagination include/exclude problems and add a better solution for building paginators --- .../TransformedDataCollectableResolver.php | 59 ++++++++++++++++++- tests/DataCollectionTest.php | 21 +++++++ 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/src/Resolvers/TransformedDataCollectableResolver.php b/src/Resolvers/TransformedDataCollectableResolver.php index f4e098cc2..a69765498 100644 --- a/src/Resolvers/TransformedDataCollectableResolver.php +++ b/src/Resolvers/TransformedDataCollectableResolver.php @@ -6,6 +6,7 @@ use Exception; use Illuminate\Contracts\Pagination\Paginator; use Illuminate\Pagination\CursorPaginator; +use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Arr; use Illuminate\Support\Enumerable; use Spatie\LaravelData\Contracts\BaseData; @@ -93,12 +94,66 @@ protected function transformPaginator( return $paginator->map(fn (BaseData $data) => $this->transformationClosure($nestedContext)($data))->all(); } - $paginated = $paginator->toArray(); + $items = array_map(fn (BaseData $data) => $this->transformationClosure($nestedContext)($data), $paginator->items()); + + ['links' => $links, 'meta' => $meta] = match ($paginator::class) { + LengthAwarePaginator::class => $this->resolveLengthAwarePaginatorLinksAndMeta($paginator), + CursorPaginator::class => $this->resolveCursorPaginatorLinksAndMeta($paginator), + default => $this->resolveUnknownPaginatorLinksAndMeta($paginator), + }; $wrapKey = $wrap->getKey() ?? 'data'; return [ - $wrapKey => array_map(fn (BaseData $data) => $this->transformationClosure($nestedContext)($data), $paginator->items()), + $wrapKey => $items, + 'links' => $links, + 'meta' => $meta, + ]; + } + + protected function resolveLengthAwarePaginatorLinksAndMeta( + LengthAwarePaginator $paginator + ): array { + return [ + 'links' => $paginator->linkCollection()->toArray(), + 'meta' => [ + 'current_page' => $paginator->currentPage(), + 'first_page_url' => $paginator->url(1), + 'from' => $paginator->firstItem(), + 'last_page' => $paginator->lastPage(), + 'last_page_url' => $paginator->url($paginator->lastPage()), + 'next_page_url' => $paginator->nextPageUrl(), + 'path' => $paginator->path(), + 'per_page' => $paginator->perPage(), + 'prev_page_url' => $paginator->previousPageUrl(), + 'to' => $paginator->lastItem(), + 'total' => $paginator->total(), + ], + ]; + } + + protected function resolveCursorPaginatorLinksAndMeta( + CursorPaginator $paginator + ): array { + return [ + 'links' => [], + 'meta' => [ + 'path' => $paginator->path(), + 'per_page' => $paginator->perPage(), + 'next_cursor' => $paginator->nextCursor()?->encode(), + 'next_page_url' => $paginator->nextPageUrl(), + 'prev_cursor' => $paginator->previousCursor()?->encode(), + 'prev_page_url' => $paginator->previousPageUrl(), + ], + ]; + } + + protected function resolveUnknownPaginatorLinksAndMeta( + Paginator|CursorPaginator $paginator, + ): array { + $paginated = $paginator->toArray(); + + return [ 'links' => $paginated['links'] ?? [], 'meta' => Arr::except($paginated, [ 'data', diff --git a/tests/DataCollectionTest.php b/tests/DataCollectionTest.php index 63cfc6d9f..2de3af204 100644 --- a/tests/DataCollectionTest.php +++ b/tests/DataCollectionTest.php @@ -8,7 +8,9 @@ use Spatie\LaravelData\DataCollection; use Spatie\LaravelData\PaginatedDataCollection; use Spatie\LaravelData\Tests\Fakes\Collections\CustomCollection; +use Spatie\LaravelData\Tests\Fakes\DummyDto; use Spatie\LaravelData\Tests\Fakes\LazyData; +use Spatie\LaravelData\Tests\Fakes\MultiLazyData; use Spatie\LaravelData\Tests\Fakes\SimpleData; it('can filter a collection', function () { @@ -276,3 +278,22 @@ expect($collection->toArray())->toBe($expect); expect($collection->toArray())->toBe($expect); }); + +it('it can include lazy items through a paginated data collection', function () { + // https://github.com/spatie/laravel-data/issues/746 + + $collection = new PaginatedDataCollection( + MultiLazyData::class, + new LengthAwarePaginator([ + DummyDto::rick(), + DummyDto::bon(), + ], 2, 15), + ); + + $filtered = $collection->through(fn (MultiLazyData $data) => $data->include('artist'))->toArray(); + + expect($filtered['data'])->toMatchArray([ + ['artist' => 'Rick Astley'], + ['artist' => 'Bon Jovi'], + ]); +});