Paginació

Com paginar resultats a Laravel: paginate, simplePaginate i cursor pagination.

Per què paginar?#

Quan una taula conté milers de registres, carregar-los tots d'un cop no és viable: consumeix massa memòria, alenteix la resposta i desborda la interfície. La paginació divideix els resultats en pàgines manejables, mostrant només un nombre limitat de registres amb controls per navegar entre pàgines.

Paginació bàsica#

El mètode paginate() és la manera més habitual de paginar resultats. Accepta el nombre de registres per pàgina i genera automàticament els enllaços de navegació:

// Al controlador
public function index()
{
    $users = User::paginate(15);
    return view('users.index', compact('users'));
}

A la vista Blade, iteres els resultats com amb qualsevol col·lecció i mostres els enllaços de paginació amb links():

@foreach($users as $user)
    <div class="card">
        <h3>{{ $user->name }}</h3>
        <p>{{ $user->email }}</p>
    </div>
@endforeach
 
{{-- Renderitza els botons de paginació --}}
{{ $users->links() }}

El mètode links() genera automàticament el HTML dels botons de paginació. Per defecte, utilitza les vistes de Tailwind CSS. Laravel llegeix el paràmetre page de la query string per determinar quina pàgina mostrar (/users?page=3).

Informació de la paginació#

L'objecte de paginació proporciona mètodes per obtenir informació útil:

<p>
    Mostrant {{ $users->firstItem() }} a {{ $users->lastItem() }}
    de {{ $users->total() }} resultats
</p>
 
<p>Pàgina {{ $users->currentPage() }} de {{ $users->lastPage() }}</p>
$users->count();          // Registres a la pàgina actual
$users->total();          // Total de registres
$users->perPage();        // Registres per pàgina
$users->currentPage();    // Pàgina actual
$users->lastPage();       // Última pàgina
$users->hasMorePages();   // Si hi ha més pàgines
$users->firstItem();      // Índex del primer registre
$users->lastItem();       // Índex de l'últim registre
$users->onFirstPage();    // Si estem a la primera pàgina

simplePaginate#

Quan tens taules molt grans, paginate() pot ser lent perquè executa un COUNT(*) per calcular el total de registres. Si no necessites mostrar el nombre total de pàgines (només botons de "Anterior" i "Següent"), utilitza simplePaginate():

$users = User::simplePaginate(15);

La diferència és que simplePaginate() no fa la consulta de recompte, cosa que el fa significativament més ràpid per a taules amb milions de registres. A canvi, no pots mostrar el total de resultats ni el nombre de pàgines.

Cursor Pagination#

Per a conjunts de dades molt grans on el rendiment és crític, la cursor pagination és la opció més eficient. En lloc d'usar OFFSET (que es torna lent amb valors alts), utilitza un cursor basat en el valor de l'últim registre:

$users = User::orderBy('id')->cursorPaginate(15);

La cursor pagination és ideal per a scroll infinit o APIs que necessiten paginar milions de registres. El desavantatge és que no pots saltar directament a una pàgina concreta: només pots anar a la pàgina anterior o a la següent.

Paginar amb Query Builder#

La paginació funciona tant amb Eloquent com amb el Query Builder directament:

// Amb Query Builder
$articles = DB::table('articles')
    ->where('published', true)
    ->orderBy('published_at', 'desc')
    ->paginate(10);
 
// Amb Eloquent i condicions
$articles = Article::query()
    ->where('published', true)
    ->with('author')
    ->latest('published_at')
    ->paginate(10);

Mantenir paràmetres de la URL#

Quan la pàgina té filtres o criteris de cerca a la query string, necessites que la paginació els mantingui. El mètode withQueryString() afegeix automàticament tots els paràmetres actuals als enllaços de paginació:

{{ $articles->withQueryString()->links() }}

Si vols afegir paràmetres específics manualment, utilitza appends():

{{ $articles->appends(['sort' => 'name', 'direction' => 'asc'])->links() }}

Per exemple, si tens una pàgina de cerca a /articles?q=laravel&category=tutorial, withQueryString() genera els enllaços com /articles?q=laravel&category=tutorial&page=2.

Personalitzar la vista de paginació#

Per defecte, Laravel utilitza vistes de Tailwind CSS per als botons de paginació. Pots canviar-ho per Bootstrap o crear les teves pròpies vistes.

Per canviar la vista globalment, configura-ho al AppServiceProvider:

use Illuminate\Pagination\Paginator;
 
public function boot(): void
{
    // Tailwind (per defecte)
    Paginator::defaultView('pagination::tailwind');
 
    // Bootstrap 5
    Paginator::useBootstrapFive();
}

Per personalitzar la vista d'una paginació concreta:

{{ $users->links('vendor.pagination.custom') }}

Per publicar i modificar les vistes de paginació:

php artisan vendor:publish --tag=laravel-pagination

Això copia les vistes a resources/views/vendor/pagination/, on pots modificar-les al teu gust.

Paginació en APIs#

Quan retornes resultats paginats des d'una API, Laravel serialitza automàticament la informació de paginació en format JSON:

// Al controlador d'API
public function index()
{
    return User::paginate(15);
}

La resposta JSON inclou les dades i tota la informació de paginació:

{
    "data": [
        {"id": 1, "name": "Joan"},
        {"id": 2, "name": "Maria"}
    ],
    "current_page": 1,
    "last_page": 10,
    "per_page": 15,
    "total": 150,
    "from": 1,
    "to": 15,
    "next_page_url": "http://app.test/api/users?page=2",
    "prev_page_url": null
}

Per personalitzar l'estructura de les dades, combina la paginació amb API Resources:

use App\Http\Resources\UserResource;
 
public function index()
{
    $users = User::paginate(15);
    return UserResource::collection($users);
}

Laravel manté automàticament les metadades de paginació quan fas servir collection() amb un paginador.