Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

franvillada's avatar

Collection Pagination with Laravel + Inertia (React)

Hi everyone!

I'm having some issues implementing pagination for a collection. So this is my relevant code:

My controller method:

public function create(): Response
    {
        // Primero obtengo el plan de cuentas
        $response = HTTP::get('http://localhost:8001/api/cuentas');
        $plan_de_cuentas = collect($response->json());

        // Después tengo que filtrar el plan de cuentas sacando aquellas que ya esten siendo utilizadas en otras partidas
        $cuentas_utilizadas = DB::table('account_budget')->get();
        $plan_de_cuentas = $plan_de_cuentas->reject(function($value,$key) use($cuentas_utilizadas){
            return $cuentas_utilizadas->contains('id_cuenta_tango',$value['ID_CUENTA']);
        });
        // Luego tengo que armar la paginacion y enviar solo la data que quiero mostrar
        $plan_de_cuentas = $plan_de_cuentas->map(function($cuenta){
            return collect($cuenta)->only('COD_CUENTA','DESC_CUENTA','TIPO_CUENTA')->toArray();
        });
        $plan_de_cuentas = CollectionHelper::paginate($plan_de_cuentas,20);
        return Inertia::render('PartidasPresupuestarias/Create',[
            'cuentas' => $plan_de_cuentas,
            'filters' => Request::only(['search'])
        ]);
    }

My CollectionHelper:

<?php


namespace App\Helpers;


use Illuminate\Container\Container;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Collection;

class CollectionHelper
{
    public static function paginate(Collection $results, $pageSize)
    {
        $page = Paginator::resolveCurrentPage('page');
        
        $total = $results->count();

        return self::paginator($results->forPage($page, $pageSize), $total, $pageSize, $page, [
            'path' => Paginator::resolveCurrentPath(),
            'pageName' => 'page',
        ]);

    }

    /**
     * Create a new length-aware paginator instance.
     *
     * @param  \Illuminate\Support\Collection  $items
     * @param  int  $total
     * @param  int  $perPage
     * @param  int  $currentPage
     * @param  array  $options
     * @return \Illuminate\Pagination\LengthAwarePaginator
     */
    protected static function paginator($items, $total, $perPage, $currentPage, $options)
    {
        return Container::getInstance()->makeWith(LengthAwarePaginator::class, compact(
            'items', 'total', 'perPage', 'currentPage', 'options'
        ));
    }
}

My Two React Components:

import React from 'react';
import Main from "../../Layouts/Main"
import {InertiaLink, usePage} from "@inertiajs/inertia-react";
import Table from "@/Components/Table/Table";
import Search from "@/Components/Search";

const Create = () => {
    const cuentas = usePage().props.cuentas.data
    console.log(cuentas)
    const titulos = ['Codigo','Cuenta','Clase']
    const filters = usePage().props.filters
    const links = usePage().props.cuentas.links
    return (
        <>
            <h2>Alta Partida Presupuestaria</h2>
            <h2>Agregar Cuenta Contable</h2>
            <div className="flex justify-between">
                <Search
                    filters={filters}
                />
            </div>
            <Table
                titulos = {titulos}
                contenido = {cuentas}
                links = {links}
            />
        </>
    );
}

Create.layout = page => <Main title="Alta Partida Presupuestaria" children={page} />

export default Create

import React from "react";
import {InertiaLink, Link} from "@inertiajs/inertia-react";

export default function Table({titulos,contenido,links}) {
    const titleItems = titulos.map((titulo) => <th key={titulo} className="px-6 py-3 text-xs font-medium leading-4 tracking-wider text-left text-gray-500 uppercase border-b border-gray-200 bg-gray-504">{titulo}</th>)
    const contentItems = contenido.map((item,key) => {
        const row = []
        Object.values(item).forEach( (value,key) => {
            if(typeof value != 'object'){
                row.push(<td key={key}>{value}</td>)
            }
        })
        return (
            <tr key={key} className="px-6 py-4 whitespace-no-wrap">{row}</tr>
        )
    })
    const paginationLinks = links.map((link) => <Link href={link.url}>{link.label}</Link>)
    return (
        <>
            <table className="min-w-full">
                <thead>
                <tr>
                    {titleItems}
                </tr>
                </thead>
                <tbody>
                {contentItems}
                </tbody>
            </table>
            <div className="mt-2">
                {paginationLinks}
            </div>
        </>
    );
}

The behaviour is really weird, basically my first page works ok and I can see all the pagination links. The problem is when I hit any other page it doesnt works because my array with the content for the table is not an array anymore is an object. If I console.log the conent on the first page that works and on the second i get the following:

Content from the first page (Working ok)

(20) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
0: {COD_CUENTA: '1.1.1.1.0.0.01', DESC_CUENTA: 'CAJA', TIPO_CUENTA: 'No monetaria'}
1: {COD_CUENTA: '1.1.1.1.0.0.02', DESC_CUENTA: 'FONDO FIJO', TIPO_CUENTA: 'No monetaria'}
2: {COD_CUENTA: '1.1.1.1.0.0.03', DESC_CUENTA: 'CAJA CHICA', TIPO_CUENTA: 'No monetaria'}
3: {COD_CUENTA: '1.1.1.1.0.0.04', DESC_CUENTA: 'EFECTIVO EN BUZON BCO. MACRO', TIPO_CUENTA: 'No monetaria'}
...
length: 20
[[Prototype]]: Array(0)

Content from the second page (Not working)

{20: {…}, 21: {…}, 22: {…}, 23: {…}, 24: {…}, 25: {…}, 26: {…}, 27: {…}, 28: {…}, 29: {…}, 30: {…}, 31: {…}, 32: {…}, 33: {…}, 34: {…}, 35: {…}, 36: {…}, 37: {…}, 38: {…}, 39: {…}}
20: {COD_CUENTA: '1.1.3.1.0.0.01', DESC_CUENTA: 'DEUDORES POR CERTIF. DE FIRMAS  - CPCES', TIPO_CUENTA: 'No monetaria'}
21: {COD_CUENTA: '1.1.3.1.0.0.02', DESC_CUENTA: 'DEUDORES POR CERTIF. DE FIRMAS  - C.S.S.', TIPO_CUENTA: 'No monetaria'}
22: {COD_CUENTA: '1.1.3.1.0.0.03', DESC_CUENTA: 'DEUDORES POR CERTIF. DE FIRMAS -  HONORARIOS PROF.', TIPO_CUENTA: 'No monetaria'}
...
[[Prototype]]: Object

Note that on the first one I have an array and on the second one an Object, so the reason it doesnt works it's obvious, I'm trying to do a map on an object so it fails.

The question is why I have array on the first page of the pagination and an object on the second page. I really dont understand.

0 likes
5 replies
schuleke's avatar

I recently stumbled over the same problem.

For me the solution was to replace:

$collection->forPage($page, $perPage)

with

$collection->slice(($page - 1) * $perPage, $perPage)->values().

Using artisan tinker I found out that the forPage method seems to behave differently based on the input.

For example if you have a collection:

$collection = collect([1, 2, 3, 4, 5, 6]);

then

$collection->forPage(1, 3); results in [ 1, 2, 3 ]

while

$collection->forPage(2, 3); results in [ 3 => 4, 4 => 5, 5 => 6 ].

I'm not sure if its just printed differently using artisan tinker since it looks fine in Xdebug but I would expect it to be same for any valid input.

Glukinho's avatar

@schuleke you can simply use ->forPage(...)->all() instead of forPage(...)

forPage() preserves original keys, that's why subsequent pages are 'object', not 'array' (their keys don't start from 0).

jlrdw's avatar

Just FYI, there are some good past post on collection paginating (not using slice) also.

schuleke's avatar

@jlrdw Can you link some of the "good" examples? Would be interesting to see if they use it differently and if their way of doing it is producing the same bug.

jlrdw's avatar

@schuleke I just do this:

https://laracasts.com/discuss/channels/guides/paginate-collection-simple-example-guide

But is is far better not to bother with collection and just use sql. Let the database do it's job.

For more results put this into a google search:

site:laracasts.com paginating a collection

Change as needed.

Slice is fine for a small set of data, but very inefficient with thousands of rows.

My advice is just use sql and paginate as needed.

Edit:

Another discussion on doing some custom paginating when in a javascript library:

https://laracasts.com/discuss/channels/vue/paginating-from-my-response

In the post it's explained a custom paginator is sometimes needed and is fairly easy to do.

Please or to participate in this conversation.