mohammad nabipour
mohammad nabipour

Reputation: 77

how to change paginate response?

In Laravel no way to replace the default structure for paginated responses. This is the structure I'm trying to achieve:

return response()->json([
    'data' => $items->items()
    'meta' => [
        'current_page'   =>   $items->currentPage(),
        'from'           =>   $items->firstItem(),
        'last_page'      =>   $items->lastPage(),
        'per_page'       =>   $items->perPage(),
        'to'             =>   $items->lastItem(),
        'total'          =>   $items->total(),
    ];
]);

Upvotes: 0

Views: 908

Answers (3)

gamedev
gamedev

Reputation: 87

I've tried to implement paginationInformation() directly into my Resource, it's never calling the paginationInformation(), any idea?

class SomeResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        //
    }

    public function paginationInformation($request, $paginated, $default)
    {

        dd("called");
    }

}

Called by using:

public function index(Request $request, OrderFilter $filters)
{
    return OrderResource::collection(Order::where('x', 'y')->paginate());
}

Upvotes: 0

Ján Janočko
Ján Janočko

Reputation: 489

In Laravel 10 (not sure since which version) you can implement method paginationInformation on your ResourceCollection (Laravel docs), where you can adjust default format of pagination data. I made class like this:

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;

class PaginatedJsonResourceCollection extends AnonymousResourceCollection
{
    /**
     * Adjust pagination data format
     *
     * @param Request $request
     * @param array $paginated
     * @param array $default
     * @return array
     */
    public function paginationInformation(Request $request, array $paginated, array $default)
    {
        unset($default['links']);
        $default['meta'] = [
            'page' => $default['meta']['current_page'],
            'lastPage' => $default['meta']['last_page'],
            'perPage' => $default['meta']['per_page'],
            'total' => $default['meta']['total']
        ];

        return $default;
    }
}

As you can see, I totally removed links attribute from the response and adjusted meta field to my needs.

Then I needed to tell Laravel to use this custom class when creating collections so I created PaginatedJsonResource class:

use Illuminate\Http\Resources\Json\JsonResource;

class PaginatedJsonResource extends JsonResource
{
    /**
     * Create a new resource collection instance.
     *
     * @param  mixed  $resource
     * @return PaginatedJsonResourceCollection
     */
    protected static function newCollection($resource): PaginatedJsonResourceCollection
    {
        return new PaginatedJsonResourceCollection($resource, static::class);
    }
}

And then made all my resources extend this class:

class ArticleResource extends PaginatedJsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @return array<string, mixed>
     */
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'content' => $this->content,
        ];
    }
}

Now whenever I return in controller

return ArticleResource::collection(Article::paginate());

I get paginated data in my custom format:

{
  "data": [
    {
      "id": 12345,
      "title": "Article 1",
      "content": "..."
    },
    {
      "id": 12346,
      "title": "Article 2",
      "content": "..."
    },
    ...
  ],
  "meta": {
    "page": 1,
    "lastPage": 50,
    "perPage": 15,
    "total": 1000
  }
}

Upvotes: 0

Kevin Bui
Kevin Bui

Reputation: 3045

I have solved this issue before with resource collection. This is not supported by Laravel out of the box so it needs a bit of work.

First, you got to override the App\Http\Resources\PaginatedResourceResponse class, which is the default way to present paginated response. Then you can override the default structure of the returned paginated data.

class CustomPaginatedResourceResponse extends PaginatedResourceResponse
{
}

Then pls create a resource collection that uses the custom paginated resource.

class ItemsResource extends ResourceCollection
{
    public function toArray($request)
    {
    }

    // Override the toResponse method.
    public function toResponse($request)
    {
        return $this->resource instanceof AbstractPaginator
            ? (new CustomPaginatedResourceResponse($this))->toResponse($request)
            : parent::toResponse($request);
    }
}

Finally, you can simply use the ItemsResource in your controller.

return new ItemsResource($items);

This requires understanding of the api resources and the source code. Pls spend some time to read the docs and figure out how the App\Http\Resources\PaginatedResourceResponse is used.

Upvotes: 1

Related Questions