Paginazione

Introduzione

Nei altri framework, la paginazione può essere molto complicata. L’approccio di Laravel alla paginazione sarà sicuramente un respiro d’aria fresca. Il paginator di Laravel è integrato con il query builder e Eloquent ORM e offre una paginazione comoda e facile da usare dei record del database senza configurazioni.

Per impostazione predefinita, l’HTML generato dal paginator è compatibile con il framework Tailwind CSS; tuttavia, è disponibile anche il supporto alla paginazione di Bootstrap.

Tailwind JIT

Se stai usando le viste di paginazione predefinite di Tailwind di Laravel e il motore Tailwind JIT, assicurati che la chiave content nel file tailwind.config.js della tua applicazione faccia riferimento alle viste di paginazione di Laravel, in modo che le loro classi Tailwind non vengano rimosse:

content: [
    './resources/**/*.blade.php',
    './resources/**/*.js',
    './resources/**/*.vue',
    './vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php',
],

Uso di base

Paginare i Risultati del Query Builder

Ci sono diversi modi per paginare gli elementi. Il più semplice è utilizzare il metodo paginate sul query builder o su una query Eloquent. Il metodo paginate si occupa automaticamente di impostare il "limit" e l’"offset" della query in base alla pagina corrente visualizzata dall’utente. Per impostazione predefinita, la pagina corrente viene rilevata dal valore dell’argomento della stringa di query page nella richiesta HTTP. Questo valore viene rilevato automaticamente da Laravel e inserito automaticamente nei link generati dal paginator.

In questo esempio, l’unico argomento passato al metodo paginate è il numero di elementi che desideri visualizzare "per pagina". In questo caso, specifichiamo che desideriamo visualizzare 15 elementi per pagina:

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;

class UserController extends Controller
{
    /**
     * Mostra tutti gli utenti dell'applicazione.
     */
    public function index(): View
    {
        return view('user.index', [
            'users' => DB::table('users')->paginate(15)
        ]);
    }
}

Paginazione Semplice

Il metodo paginate conta il numero totale di record corrispondenti alla query prima di recuperarli dal database. Questo permette al paginator di sapere quante pagine di record esistono in totale. Tuttavia, se non prevedi di mostrare il numero totale di pagine nell’interfaccia della tua applicazione, la query per contare i record non è necessaria.

Quindi, se hai bisogno solo di mostrare semplici link "Successivo" e "Precedente" nell’interfaccia della tua applicazione, puoi utilizzare il metodo simplePaginate per eseguire una query unica ed efficiente:

    $users = DB::table('users')->simplePaginate(15);

Paginare i risultati di Eloquent

Puoi anche paginare le query Eloquent. In questo esempio, pagineremo il modello App\Models\User e specificheremo che vogliamo mostrare 15 record per pagina. Come vedi, la sintassi è quasi identica a quella per paginare i risultati del query builder:

    use App\Models\User;

    $users = User::paginate(15);

Naturalmente, puoi chiamare il metodo paginate dopo aver impostato altri vincoli sulla query, come clausole where:

    $users = User::where('votes', '>', 100)->paginate(15);

Puoi anche utilizzare il metodo simplePaginate quando pagini modelli Eloquent:

    $users = User::where('votes', '>', 100)->simplePaginate(15);

Allo stesso modo, puoi usare il metodo cursorPaginate per paginare con cursore i modelli Eloquent:

    $users = User::where('votes', '>', 100)->cursorPaginate(15)

Più istanze di paginator per pagina

A volte potresti aver bisogno di mostrare due paginator separati in una singola schermata gestita dalla tua applicazione. Tuttavia, se entrambe le istanze di paginator usano il parametro della query string page per memorizzare la pagina corrente, i due paginator entreranno in conflitto. Per risolvere questo conflitto, puoi passare il nome del parametro della query string che desideri utilizzare per memorizzare la pagina corrente del paginator tramite il terzo argomento fornito ai metodi paginate, simplePaginate e cursorPaginate:

    use App\Models\User;

    $users = User::where('votes', '>', 100)->paginate(
        $perPage = 15, $columns = ['*'], $pageName = 'users'
    );

Paginazione con Cursor

Mentre paginate e simplePaginate creano query utilizzando la clausola "offset" di SQL, la paginazione con cursor funziona costruendo clausole "where" che confrontano i valori delle colonne ordinate presenti nella query, offrendo le prestazioni del database più efficienti disponibili tra tutti i metodi di paginazione di Laravel. Questo metodo di paginazione è particolarmente adatto per grandi insiemi di dati e interfacce utente con scorrimento "infinito".

A differenza della paginazione basata su offset, che include un numero di pagina nella stringa di query degli URL generati dal paginator, la paginazione basata su cursor inserisce una stringa "cursor" nella stringa di query. Il cursor è una stringa codificata che contiene la posizione da cui la prossima query paginata dovrebbe iniziare a paginare e la direzione in cui dovrebbe paginare:

http://localhost/users?cursor=eyJpZCI6MTUsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0

Puoi creare un’istanza paginator basata su cursor tramite il metodo cursorPaginate offerto dal query builder. Questo metodo restituisce un’istanza di Illuminate\Pagination\CursorPaginator:

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

Una volta ottenuta un’istanza del paginator con cursor, puoi mostrare i risultati della paginazione come faresti normalmente utilizzando i metodi paginate e simplePaginate. Per ulteriori informazioni sui metodi dell’istanza offerti dal cursor paginator, consulta la documentazione dei metodi dell’istanza del cursor paginator.

La tua query deve contenere una clausola "order by" per usufruire della paginazione con cursor. Inoltre, le colonne con cui la query è ordinata devono appartenere alla tabella che stai paginando.

Paginazione con Cursor vs Offset

Per illustrare le differenze tra la paginazione offset e quella con cursor, esaminiamo alcune query SQL di esempio. Entrambe le seguenti query mostreranno la "seconda pagina" di risultati per una tabella users ordinata per id:

# Paginazione Offset
select * from users order by id asc limit 15 offset 15;
# Paginazione con cursore...
select * from users where id > 15 order by id asc limit 15;

La query di paginazione con cursore offre i seguenti vantaggi rispetto alla paginazione con offset:

  • Per grandi dataset, la paginazione con cursore offrirà migliori prestazioni se le colonne "order by" sono indicizzate. Questo perché la clausola "offset" scansiona tutti i dati precedentemente corrispondenti.
  • Per dataset con scritture frequenti, la paginazione con offset potrebbe saltare record o mostrare duplicati se i risultati sono stati recentemente aggiunti o eliminati dalla pagina che l’utente sta visualizzando.

Tuttavia, la paginazione con cursore ha le seguenti limitazioni:

  • Come simplePaginate, la paginazione con cursore può essere utilizzata solo per mostrare i link "Next" e "Previous" e non supporta la generazione di link con numeri di pagina.
  • Richiede che l’ordinamento sia basato su almeno una colonna unica o su una combinazione di colonne uniche. Le colonne con valori null non sono supportate.
  • Le espressioni di query nelle clausole "order by" sono supportate solo se sono alias e aggiunte anche alla clausola "select".
  • Le espressioni di query con parametri non sono supportate.

Creare Manualmente un Paginator

A volte potresti voler creare un’istanza di paginazione manualmente, passando un array di elementi che hai già in memoria. Puoi farlo creando un’istanza di Illuminate\Pagination\Paginator, Illuminate\Pagination\LengthAwarePaginator o Illuminate\Pagination\CursorPaginator, a seconda delle tue esigenze.

Le classi Paginator e CursorPaginator non hanno bisogno di conoscere il numero totale di elementi nel set di risultati; tuttavia, per questo motivo, non dispongono di metodi per ottenere l’indice dell’ultima pagina. Il LengthAwarePaginator accetta quasi gli stessi argomenti del Paginator, ma richiede un conteggio totale degli elementi nel set di risultati.

In altre parole, il Paginator corrisponde al metodo simplePaginate del query builder, il CursorPaginator al metodo cursorPaginate e il LengthAwarePaginator al metodo paginate.

Quando crei manualmente un’istanza di paginator, dovresti "tagliare" manualmente l’array dei risultati che passi al paginator. Se non sei sicuro di come farlo, consulta la funzione PHP array_slice.

Personalizzare gli URL di Paginazione

Per impostazione predefinita, i link generati dal paginatore corrisponderanno all’URI della richiesta corrente. Tuttavia, il metodo withPath del paginatore ti permette di personalizzare l’URI utilizzato quando vengono generati i link. Ad esempio, se vuoi che il paginatore generi link come http://example.com/admin/users?page=N, devi passare /admin/users al metodo withPath:

    use App\Models\User;

    Route::get('/users', function () {
        $users = User::paginate(15);

        $users->withPath('/admin/users');

        // ...
    });

Aggiungere Valori alla Query String

Puoi aggiungere alla query string dei link di paginazione usando il metodo appends. Ad esempio, per aggiungere sort=votes a ogni link di paginazione, dovresti chiamare appends nel seguente modo:

use App\Models\User;

Route::get('/users', function () {
    $users = User::paginate(15);

    $users->appends(['sort' => 'votes']);

    // ...
});

Puoi usare il metodo withQueryString se desideri aggiungere tutti i valori della query string della richiesta corrente ai link di paginazione:

$users = User::paginate(15)->withQueryString();

Aggiungere frammenti hash

Se hai bisogno di aggiungere un "frammento hash" agli URL generati dal paginator, puoi usare il metodo fragment. Ad esempio, per aggiungere #users alla fine di ogni link di paginazione, devi chiamare il metodo fragment in questo modo:

    $users = User::paginate(15)->fragment('users');

Visualizzazione dei Risultati di Paginazione

Quando chiami il metodo paginate, riceverai un’istanza di Illuminate\Pagination\LengthAwarePaginator, mentre chiamando il metodo simplePaginate restituisce un’istanza di Illuminate\Pagination\Paginator. Infine, chiamando il metodo cursorPaginate ottieni un’istanza di Illuminate\Pagination\CursorPaginator.

Questi oggetti forniscono diversi metodi che descrivono il set di risultati. Oltre a questi metodi di supporto, le istanze del paginator sono iterabili e possono essere percorse come un array. Quindi, una volta recuperati i risultati, puoi mostrarli e renderizzare i link delle pagine usando Blade:

<div class="container">
    @foreach ($users as $user)
        {{ $user->name }}
    @endforeach
</div>

{{ $users->links() }}

Il metodo links renderizzerà i link alle altre pagine del set di risultati. Ognuno di questi link conterrà già la corretta variabile di query string page. Ricorda, l’HTML generato dal metodo links è compatibile con il framework Tailwind CSS.

Regolare la Finestra dei Link di Paginazione

Quando il paginator mostra i link di paginazione, viene visualizzato il numero della pagina corrente insieme ai link per le tre pagine prima e dopo. Utilizzando il metodo onEachSide, puoi controllare quanti link aggiuntivi vengono mostrati su ciascun lato della pagina corrente all’interno della finestra centrale mobile di link generata dal paginator:

{{ $users->onEachSide(5)->links() }}

Conversione dei Risultati in JSON

Le classi paginator di Laravel implementano il contratto dell’interfaccia Illuminate\Contracts\Support\Jsonable ed espongono il metodo toJson, quindi è molto semplice convertire i tuoi risultati di paginazione in JSON. Puoi anche convertire un’istanza di paginator in JSON restituendola da una route o da un’azione di un controller:

    use App\Models\User;

    Route::get('/users', function () {
        return User::paginate();
    });

Il JSON del paginator includerà informazioni meta come total, current_page, last_page e altro. I record dei risultati sono disponibili tramite la chiave data nell’array JSON. Ecco un esempio del JSON creato restituendo un’istanza di paginator da una route:

    {
       "total": 50,
       "per_page": 15,
       "current_page": 1,
       "last_page": 4,
       "first_page_url": "http://laravel.app?page=1",
       "last_page_url": "http://laravel.app?page=4",
       "next_page_url": "http://laravel.app?page=2",
       "prev_page_url": null,
       "path": "http://laravel.app",
       "from": 1,
       "to": 15,
       "data":[
            {
                // Record...
            },
            {
                // Record...
            }
       ]
    }

Personalizzazione della Vista di Paginazione

Di default, le viste renderizzate per visualizzare i link di paginazione sono compatibili con il framework Tailwind CSS. Tuttavia, se non usi Tailwind, puoi definire le tue viste per renderizzare questi link. Quando chiami il metodo links su un’istanza paginator, puoi passare il nome della vista come primo argomento al metodo:

{{ $paginator->links('view.name') }}

<!-- Passing additional data to the view... -->
{{ $paginator->links('view.name', ['foo' => 'bar']) }}

Tuttavia, il modo più semplice per personalizzare le viste di paginazione è esportarle nella directory resources/views/vendor usando il comando vendor:publish:

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

Questo comando posizionerà le viste nella directory resources/views/vendor/pagination della tua applicazione. Il file tailwind.blade.php all’interno di questa directory corrisponde alla vista di paginazione di default. Puoi modificare questo file per cambiare l’HTML della paginazione.

Se desideri designare un file diverso come vista di paginazione predefinita, puoi invocare i metodi defaultView e defaultSimpleView del paginator all’interno del metodo boot della tua classe App\Providers\AppServiceProvider:

<?php

namespace App\Providers;

use Illuminate\Pagination\Paginator;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        Paginator::defaultView('view-name');

        Paginator::defaultSimpleView('view-name');
    }
}

Utilizzo di Bootstrap

Laravel include le viste di paginazione costruite con Bootstrap CSS. Per utilizzare queste viste invece delle viste predefinite di Tailwind, puoi chiamare i metodi useBootstrapFour o useBootstrapFive del paginator all’interno del metodo boot della tua classe App\Providers\AppServiceProvider:

use Illuminate\Pagination\Paginator;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Paginator::useBootstrapFive();
    Paginator::useBootstrapFour();
}

Metodi di Istanza di Paginator / LengthAwarePaginator

Ogni istanza di paginator fornisce informazioni aggiuntive sulla paginazione tramite i seguenti metodi:

Metodo Descrizione
$paginator->count() Ottiene il numero di elementi per la pagina corrente.
$paginator->currentPage() Ottiene il numero della pagina corrente.
$paginator->firstItem() Ottiene il numero del primo elemento nei risultati.
$paginator->getOptions() Ottiene le opzioni del paginator.
$paginator->getUrlRange($start, $end) Crea un intervallo di URL di paginazione.
$paginator->hasPages() Verifica se ci sono abbastanza elementi per suddividerli in più pagine.
$paginator->hasMorePages() Verifica se ci sono più elementi nell’archivio dati.
$paginator->items() Ottiene gli elementi della pagina corrente.
$paginator->lastItem() Ottiene il numero dell’ultimo elemento nei risultati.
$paginator->lastPage() Ottiene il numero dell’ultima pagina disponibile. (Non disponibile quando si usa simplePaginate).
$paginator->nextPageUrl() Ottiene l’URL per la pagina successiva.
$paginator->onFirstPage() Verifica se il paginator si trova sulla prima pagina.
$paginator->perPage() Il numero di elementi da mostrare per pagina.
$paginator->previousPageUrl() Ottiene l’URL per la pagina precedente.
$paginator->total() Determina il numero totale di elementi corrispondenti nell’archivio dati. (Non disponibile quando si usa simplePaginate).
$paginator->url($page) Ottiene l’URL per un numero di pagina specifico.
$paginator->getPageName() Ottiene la variabile della query string usata per memorizzare la pagina.
$paginator->setPageName($name) Imposta la variabile della query string usata per memorizzare la pagina.
$paginator->through($callback) Trasforma ogni elemento usando una callback.

Metodi delle Instance del Cursor Paginator

Ogni istanza del cursor paginator fornisce informazioni aggiuntive sulla paginazione tramite i seguenti metodi:

Metodo Descrizione
$paginator->count() Ottieni il numero di elementi per la pagina corrente.
$paginator->cursor() Ottieni l’istanza del cursore attuale.
$paginator->getOptions() Ottieni le opzioni del paginator.
$paginator->hasPages() Verifica se ci sono abbastanza elementi per dividere in più pagine.
$paginator->hasMorePages() Verifica se ci sono altri elementi nel data store.
$paginator->getCursorName() Ottieni la variabile della query string utilizzata per memorizzare il cursore.
$paginator->items() Ottieni gli elementi per la pagina corrente.
$paginator->nextCursor() Ottieni l’istanza del cursore per il set successivo di elementi.
$paginator->nextPageUrl() Ottieni l’URL per la pagina successiva.
$paginator->onFirstPage() Verifica se il paginator si trova sulla prima pagina.
$paginator->onLastPage() Verifica se il paginator si trova sull’ultima pagina.
$paginator->perPage() Il numero di elementi da mostrare per pagina.
$paginator->previousCursor() Ottieni l’istanza del cursore per il set precedente di elementi.
$paginator->previousPageUrl() Ottieni l’URL per la pagina precedente.
$paginator->setCursorName() Imposta la variabile della query string utilizzata per memorizzare il cursore.
$paginator->url($cursor) Ottieni l’URL per una data istanza del cursore.
Lascia un commento

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *