spycrabo's avatar

Pagination inside JsonResource

Is there any ready mechanism in Laravel to organize pagination inside json resources?

For example we have some models structure - Parent has many Children. Now we need to create API method which would return collection of Parent models and each Parent model will contain its Children collection. For sure we are using pagination for Parent collection and return json like:

{
  "data": [
    // Json objects of Parent models:
    {
      "id": 0,
      "title": "smth",
      "children": [
        // json objects of Children models
      ],
    }
  ],
 "meta": {
    "page": 1,
    "total": 400,
    ...
  },
}

Of cource this API methods also supports eager loading from query parameters, so JsonResouce for Parent looks like:

public function toArray()
{
  return [
    "id" => $this->id,
    "title" => $this->title,
    "children" => new ChildResourceCollection($this->whenLoaded('children')),
  ];
}

Then we remember that Parent has many Children. And "has many" means really many: for example more than 100. So in that case we need to use pagination for Children inside Parent. For limiting eager loaded data with can use closures in with() method:

Parent::query()
  ->with([
    'children' => fn($query) => $query->limit(10),
  ])

But this implementation will not containt all required pagination info (for example, total children count). In that case we don't need to return simple Collection instance for relation, but the LenthAwarePaginator.

In conslusion: I need to make API method api/parents, which can accept include query parameter for parsing eager loaded relations and eager loaded relations must return also their pagination info. Final response should look like that:

{
    "data": [
        {
            "id": 1,
            "title": "first",
            "children": {
                "data": [
                    {
                        "id": 1,
                        "title": "child1"
                    },
                    {
                        "id": 2,
                        "title": "child2"
                    }
                    // ... and more other children
                ],
                "meta": {
                    "total": 100,
                    "page": 1,
                    "per_page": 10
                }
            }
        }
        // ... and more other parents
    ],
    "meta": {
        "total": 500,
        "page": 1,
        "per_page": 10
    }
}
0 likes
1 reply
automica's avatar

You can just use paginate.

eg

    /**
     * Display a listing of the resource.
     *
     * @return AnonymousResourceCollection
     */
    public function __invoke(): AnonymousResourceCollection
    {
        return ActivityTypeResource::collection(ActivityType::paginate());
    }

adds the following to your json response:

"links": {
    "first": "https://your-api.com/api/v1/activity-types?page=1",
        "last": "https://your-api.com/api/v1/activity-types?page=1",
        "prev": null,
        "next": null
    },
    "meta": {
    "current_page": 1,
        "from": 1,
        "last_page": 1,
        "links": [
            {
                "url": null,
                "label": "« Previous",
                "active": false
            },
            {
                "url": "https://your-api.com/api/v1/activity-types?page=1",
                "label": "1",
                "active": true
            },
            {
                "url": null,
                "label": "Next »",
                "active": false
            }
        ],
        "path": "https://your-api.com/api/v1/activity-types",
        "per_page": 15,
        "to": 13,
        "total": 13
    }
1 like

Please or to participate in this conversation.