3

Laravel default paginate gave me the response with default format for pagination, but I want to remove links in the meta object at the page response

I used below code to fetch the page data:

public function index()
{
  return response()->json(
     new EntityCollection(Entity::paginate($pageSize))
  );
}

It return the response in resource collection I called EntityCollection in my codes. But I want to remove links in the meta at the response.

EntityCollection looks like this:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\ResourceCollection;

class EntityCollection extends ResourceCollection
 {

 /**
  * Transform the resource collection into an array.
  *
  * @param  \Illuminate\Http\Request  $request
  * @return array
  */
  public function toArray($request)
  {
     return [
        'data' => $this->collection,
     ];

  }
}

When I use EntityCollection to fetch the list it returns below format response:

{
"data": [
    // data
],
"links": {
    "first": "*url?page_size=1&page=1",
    "last": "*url?page_size=1&page=15",
    "prev": null,
    "next": "*url?page_size=1&page=2"
},
"meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 15,
    "links": [
        {
            "url": null,
            "label": "pagination.previous",
            "active": false
        },
        {
            "url": "*url?page_size=1&page=6",
            "label": 6,
            "active": false
        }
   ],
    "path": "*url",
    "per_page": "1",
    "to": 1,
    "total": 15
 }
}

Please give me the way to remove links in the meta or the best practice to customise the response of paginate in Laravel.

  • check this https://stackoverflow.com/questions/48094741/customising-laravel-5-5-api-resource-collection-pagination/50032477#50032477 – Martin Osusky Mar 09 '21 at 13:56

5 Answers5

2

My solution:


<?php

namespace App\Core\Resources;

use Illuminate\Http\Resources\Json\AnonymousResourceCollection;

class AppAnonymousResourceCollection extends AnonymousResourceCollection
{
    public function paginationInformation($request, $paginated, $default): array
    {
        return [
            'pagination' => [
                'currentPage' => $paginated['current_page'],
                'from' => $paginated['from'],
                'lastPage' => $paginated['last_page'],
                'perPage' => $paginated['per_page'],
                'to' => $paginated['to'],
                'total' => $paginated['total'],
            ]
        ];
    }
}

<?php

namespace App\Core\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class AppJsonResource extends JsonResource
{
    public static function collection($resource)
    {
        return tap(new AppAnonymousResourceCollection($resource, static::class), function ($collection) {
            if (property_exists(static::class, 'preserveKeys')) {
                $collection->preserveKeys = (new static([]))->preserveKeys === true;
            }
        });
    }
}

class AcmeResource extends AppJsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  Request| null  $request
     * @return array
     */
    public function toArray($request = null): array
    {
        return [
            ...
        ];
    }
}

On controller..

return AcmeResource::collection($collection->paginate($limit))
            ->response();

Result:

{
    "data": [
        ...
    ],
    "pagination": {
        "currentPage": 2,
        "from": 4,
        "lastPage": 7,
        "perPage": 3,
        "to": 6,
        "total": 21
    }
}
Pablo Papalardo
  • 1,224
  • 11
  • 9
1

It is simple by creating this method from your own collection, EntityCollection:

public function paginationInformation($request, $paginated, $default)
{
    unset($default['links']);
    return $default;
}
Phuc Pham
  • 358
  • 1
  • 3
  • 11
0

The problem here is that the ResourceCollection overrides your custom response structure and adds some extra properties here. You can fix this (broken IMO) behavior by overriding the toResponse() method like so:

/**
 * {@inheritdoc}
 */
public function toResponse($request)
{
    return JsonResource::toResponse($request);
}
Kolyunya
  • 5,973
  • 7
  • 46
  • 81
0

I came upon this question while trying to do the same. With Laravel 8, it seems you can do this, but you can't use the resource collection. You simply use the resource class you created. So, in your case, instead of using EntityCollection, you use EntityResource

Sample below:

$results = EntityResource::collection(Entity::paginate($pageSize));

return response()->json([
    'data' => $results,
    'lastPage' => $results->lastPage()
]);

With this approach, you essentially build the metadata yourself. You can reference Laravel's pagination documentation to see what other methods are available for building out your own metadata.

https://laravel.com/docs/8.x/pagination

David Tran
  • 104
  • 1
  • 5
0

This was always a PIA for me.

The suggestions / answers you will find will involve the creation of multiple collections, probable undefined keys if you use the wrong paginate method, etc.

What I like to do is simple, I add one more function to an Eloquent Builder, so you can just do jsonPaginate().

app/Providers/AppServiceProvider.php

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;

public function boot()
{
    Builder::macro('jsonPaginate', function (int $perPage = 15) {
        /** @var Collection $this */
        $json = $this->paginate($perPage)->toArray();
        $json['meta'] = [
            'from'      => $json['from'],
            'to'        => $json['to'],
            'last_page' => $json['last_page'],
            'per_page'  => $json['per_page'],
            'total'     => $json['total'],
        ];
        unset(
            $json['links'],
            $json['path'],
            $json['prev_page_url'],
            $json['first_page_url'],
            $json['last_page_url'],
            $json['next_page_url'],
            $json['current_page'],
            $json['from'],
            $json['to'],
            $json['last_page'],
            $json['per_page'],
            $json['total'],
        );
        return $json;
    });
}

Now you can just use it like this:

Route::get('/', function () {
    return App\Models\User::jsonPaginate();
});

PS.:

  • The DocBlock is to bypass the Intelephense error when grabbing $this from an anonymous function / callback.
  • I've used in my example two customizations, one is to "move" some keys into a meta key, the other one is to remove the unwanted ones, you can customize it the way you want.