mathewparet's avatar

Eloquent Pagination and Scout Pagination returns different structures?

When I try pagination with Eloquent (when returning all records) and Scout pagination (when returning searched data) has different data structure! Is it how it is or am I doing something wrong?

This is my controller method:

public function index(Request $request)
    {
        Gate::authorize('viewAny', Link::class);

        if(!$request->has('filter') || empty($request->get('filter')))
            return Inertia::render('Links/Index', [
                'links' => Inertia::defer(fn() => new LinkResourceCollection($request->user()->links()->latest()->withCount('clicks')->paginate(5)))->merge(),
            ]);

        $links = Link::search($request->get('filter'))->where('user_id', $request->user()->id)->paginate(5);

        return compact('links');
    }

BTW another thing I noticed is that resource collection works with eloquent pagination, but when I use scout pagination the pagination data is somehow stripped when I use resource collection.

0 likes
1 reply
LaryAI's avatar
Level 58

When using Eloquent's pagination and Laravel Scout's pagination, it's expected to see differences in the data structure returned. This is because Eloquent's pagination and Scout's pagination are designed to handle different types of data retrieval and have different underlying implementations.

Differences in Structure

  1. Eloquent Pagination:

    • Returns an instance of Illuminate\Pagination\LengthAwarePaginator.
    • Includes pagination metadata such as total, per_page, current_page, last_page, etc.
    • The data is typically accessed via the data key.
  2. Scout Pagination:

    • Returns an instance of Laravel\Scout\Builder which is then paginated.
    • The structure might differ slightly, especially if using a search engine like Algolia or Meilisearch, which might not include all the metadata in the same format.

Solution

To handle these differences, you can normalize the data structure in your controller before returning it to the view. Here's how you can adjust your controller method:

public function index(Request $request)
{
    Gate::authorize('viewAny', Link::class);

    if (!$request->has('filter') || empty($request->get('filter'))) {
        $links = $request->user()->links()->latest()->withCount('clicks')->paginate(5);
    } else {
        $links = Link::search($request->get('filter'))
            ->where('user_id', $request->user()->id)
            ->paginate(5);
    }

    // Normalize the pagination structure
    $links = [
        'data' => $links->items(),
        'current_page' => $links->currentPage(),
        'last_page' => $links->lastPage(),
        'per_page' => $links->perPage(),
        'total' => $links->total(),
    ];

    return Inertia::render('Links/Index', [
        'links' => $links,
    ]);
}

Resource Collection Issue

If you're experiencing issues with resource collections stripping pagination data when using Scout, ensure that your resource collection is correctly handling the pagination metadata. You might need to manually add pagination data to your resource collection if it's not automatically included.

Here's a basic example of how you might adjust your resource collection:

class LinkResourceCollection extends ResourceCollection
{
    public function toArray($request)
    {
        return [
            'data' => $this->collection,
            'meta' => [
                'current_page' => $this->currentPage(),
                'last_page' => $this->lastPage(),
                'per_page' => $this->perPage(),
                'total' => $this->total(),
            ],
        ];
    }
}

By normalizing the data structure and ensuring your resource collections are correctly configured, you can handle the differences between Eloquent and Scout pagination more effectively.

Please or to participate in this conversation.