← Nicola VitraniScrivimi

Note dal lavoro · Backend

Paginazione in Laravel: offset o cursore?

Backend · 4 min di lettura

Laravel offre tre modi di paginare un risultato. Cambiano il costo della query e cosa può fare l’utente, quindi vale la pena scegliere con intenzione.

I tre metodi

  • paginate(): restituisce anche il totale e i numeri di pagina. Esegue una query in più per contare le righe.
  • simplePaginate(): solo «precedente» e «successivo», senza conteggio totale, quindi una query in meno.
  • cursorPaginate(): usa un cursore (il valore dell’ultima riga vista) invece di un offset. Niente numeri di pagina, ma costo costante.

Perché l’offset peggiora

Una query con LIMIT 20 OFFSET 100000 costringe il database a leggere e scartare centomila righe prima di restituire le venti che servono: più vai avanti, più è lenta. In più, se nel frattempo vengono inserite o cancellate righe, tra una pagina e l’altra si possono vedere duplicati o saltare elementi.

Come funziona il cursore

// ordina per una colonna indicizzata e univoca (o con un tie-breaker)
$posts = Post::orderBy('id')->cursorPaginate(25);

Invece di «salta 100000 righe» la query diventa «dammi le 25 con id maggiore di quello dell’ultima pagina». Con un indice sulla colonna di ordinamento è veloce a qualsiasi profondità, e non risente delle righe aggiunte nel frattempo.

  • Serve un ordinamento stabile: colonne univoche o una combinazione con l’id come spareggio.
  • Non puoi saltare alla pagina 37: si procede avanti e indietro.
  • La colonna di ordinamento deve avere un indice.

Quale scegliere

  • Tabelle di amministrazione con numeri di pagina e pochi dati: paginate() va benissimo.
  • Elenchi lunghi dove il totale non serve: simplePaginate().
  • Feed, scroll infinito, API pubbliche e dataset che crescono: cursorPaginate().

Se non sei sicuro, misura: attiva il log delle query e confronta tempi alla pagina uno e a una pagina profonda con dati realistici.