Passer au contenu principal

Qu’est-ce que la pagination

La pagination de Laravel est intégrée au query builder et à l’ORM Eloquent : aucune configuration n’est nécessaire. La page courante est extraite automatiquement du paramètre page de la requête HTTP et ajoutée aux liens générés. Le HTML par défaut est compatible avec Tailwind CSS ; Bootstrap est aussi disponible.

Trois types de pagination

MéthodeRetourCaractéristiques
paginate()LengthAwarePaginatorRécupère le total. Génère des liens numérotés
simplePaginate()PaginatorNe récupère pas le total. Uniquement « Précédent » / « Suivant »
cursorPaginate()CursorPaginatorBasée sur un curseur. Idéale pour de grands volumes

Utilisation de base

Pagination du query builder

use Illuminate\Support\Facades\DB;

// 15 éléments par page
$users = DB::table('users')->paginate(15);

Pagination Eloquent

use App\Models\User;

$users = User::paginate(15);

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

simplePaginate

Si la requête COUNT(*) du total n’est pas nécessaire (uniquement liens « précédent »/« suivant »), utilisez simplePaginate() pour plus d’efficacité.
$users = DB::table('users')->simplePaginate(15);
$users = User::where('active', true)->simplePaginate(15);
Si vous n’avez pas besoin d’afficher « X sur Y », choisissez simplePaginate(). paginate() exécute une requête COUNT(*) supplémentaire, donc simplePaginate() est plus rapide.

cursorPaginate (pagination par curseur)

La pagination par curseur remplace OFFSET par une clause WHERE, ce qui offre de bonnes performances pour de grandes quantités de données. Idéale pour les UI en défilement infini.
// orderBy est obligatoire
$users = DB::table('users')->orderBy('id')->cursorPaginate(15);
$users = User::where('active', true)->cursorPaginate(15);
Les URL générées contiennent une chaîne de curseur au lieu d’un numéro de page.
http://example.com/users?cursor=eyJpZCI6MTUsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0
La pagination par curseur exige orderBy. De plus, la colonne d’ordre doit appartenir à la table paginée.

Comparaison OFFSET vs. curseur

-- paginate() / simplePaginate() — utilise OFFSET
SELECT * FROM users ORDER BY id ASC LIMIT 15 OFFSET 15;

-- cursorPaginate() — utilise WHERE (bénéficie des index)
SELECT * FROM users WHERE id > 15 ORDER BY id ASC LIMIT 15;
La pagination par curseur exploite bien les index, et évite les doublons/oublis lorsque les données sont modifiées fréquemment. En revanche, elle ne propose pas de liens numérotés, seulement « Précédent » / « Suivant ».

Implémentation dans un contrôleur

<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\View\View;

class UserController extends Controller
{
    public function index(): View
    {
        $users = User::orderBy('name')->paginate(20);

        return view('users.index', compact('users'));
    }
}

Affichage des liens de pagination dans Blade

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

{{-- Affichage des liens (compatible Tailwind CSS) --}}
{{ $users->links() }}
La méthode links() génère automatiquement le HTML des liens. Par défaut, trois pages avant et trois pages après la page courante sont affichées.

Ajuster le nombre de liens affichés

onEachSide() permet de modifier le nombre de liens de chaque côté.
{{-- Affiche 5 pages de chaque côté de la page courante --}}
{{ $users->onEachSide(5)->links() }}

Nombre d’éléments par page passé par la requête

public function index(Request $request): View
{
    $perPage = $request->integer('per_page', 15);
    $perPage = min(max($perPage, 1), 100); // Limité entre 1 et 100

    $users = User::paginate($perPage);

    return view('users.index', compact('users'));
}

Plusieurs paginateurs sur la même page

Si deux paginateurs cohabitent, ils entreront en conflit sur le paramètre page. Renommez-le via le troisième argument.
$users = User::paginate(
    perPage: 15,
    columns: ['*'],
    pageName: 'users'
);

$posts = Post::paginate(
    perPage: 10,
    columns: ['*'],
    pageName: 'posts'
);

Personnalisation de l’URL

Modifier l’URL de base

$users = User::paginate(15);

// URLs de la forme /admin/users?page=N
$users->withPath('/admin/users');

Ajouter des paramètres de requête

// Ajoute sort=votes à chaque lien
$users->appends(['sort' => 'votes']);

// Reprend tous les paramètres de la requête courante
$users->withQueryString();

Ajouter un fragment de hachage

// Ajoute #users à la fin des URL
$users->fragment('users');

Réponse API (JSON)

Retourner un paginateur directement depuis une route ou un contrôleur le convertit automatiquement en JSON.
use App\Models\User;

Route::get('/api/users', function () {
    return User::paginate(15);
});
Format JSON de la réponse :
{
    "total": 50,
    "per_page": 15,
    "current_page": 1,
    "last_page": 4,
    "first_page_url": "http://example.com/api/users?page=1",
    "last_page_url": "http://example.com/api/users?page=4",
    "next_page_url": "http://example.com/api/users?page=2",
    "prev_page_url": null,
    "path": "http://example.com/api/users",
    "from": 1,
    "to": 15,
    "data": [
        { "id": 1, "name": "Taro Yamada" },
        { "id": 2, "name": "Hanako Suzuki" }
    ]
}

Combinaison avec les API Resources

Pour envelopper le résultat de paginate() dans une collection de resource API, passez-le à UserResource::collection().
use App\Http\Resources\UserResource;
use App\Models\User;

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

    return UserResource::collection($users);
});
Les informations de pagination sont automatiquement ajoutées comme métadonnées.
Le JSON de cursorPaginate() contient next_cursor et prev_cursor au lieu de numéros de page. Le client API doit renvoyer ces valeurs dans le paramètre cursor de la requête suivante.

Vues de pagination personnalisées

Spécifier la vue directement

{{-- Utilise une vue personnalisée --}}
{{ $paginator->links('vendor.pagination.custom') }}

{{-- Passer des données supplémentaires --}}
{{ $paginator->links('vendor.pagination.custom', ['theme' => 'dark']) }}

Remplacer la vue par défaut

Publiez d’abord les vues officielles, puis personnalisez-les.
php artisan vendor:publish --tag=laravel-pagination
Les fichiers suivants sont générés dans resources/views/vendor/pagination/ :
  • tailwind.blade.php — Par défaut (Tailwind CSS)
  • bootstrap-5.blade.php — Bootstrap 5
  • simple-tailwind.blade.php — Pour simplePaginate
Modifiez tailwind.blade.php ou créez une nouvelle vue et déclarez-la dans AppServiceProvider.
use Illuminate\Pagination\Paginator;

public function boot(): void
{
    Paginator::defaultView('vendor.pagination.custom');
    Paginator::defaultSimpleView('vendor.pagination.simple-custom');
}

Utiliser Bootstrap CSS

Pour utiliser Bootstrap au lieu de Tailwind, déclarez-le dans boot() du AppServiceProvider.
use Illuminate\Pagination\Paginator;

public function boot(): void
{
    Paginator::useBootstrapFive(); // Bootstrap 5
    // Paginator::useBootstrapFour(); // Bootstrap 4
}

Créer manuellement un paginateur

Pour paginer des données existantes (par exemple un tableau), instanciez directement la classe.
use Illuminate\Pagination\LengthAwarePaginator;

$items = collect(range(1, 200))->map(fn ($i) => ['id' => $i, 'name' => "Élément {$i}"]);

$perPage = 15;
$currentPage = request()->integer('page', 1);

$paginator = new LengthAwarePaginator(
    items: $items->forPage($currentPage, $perPage),
    total: $items->count(),
    perPage: $perPage,
    currentPage: $currentPage,
    options: ['path' => request()->url()]
);

Méthodes courantes de l’instance

$paginator = User::paginate(15);

$paginator->currentPage();     // Numéro de la page courante
$paginator->lastPage();        // Dernière page (indisponible avec simplePaginate)
$paginator->total();           // Total (indisponible avec simplePaginate)
$paginator->perPage();         // Nombre par page
$paginator->count();           // Nombre d'éléments sur la page courante
$paginator->firstItem();       // Numéro du premier élément
$paginator->lastItem();        // Numéro du dernier élément
$paginator->hasPages();        // Y a-t-il plusieurs pages ?
$paginator->hasMorePages();    // Y a-t-il une page suivante ?
$paginator->onFirstPage();     // Est-ce la première page ?
$paginator->onLastPage();      // Est-ce la dernière page ?
$paginator->nextPageUrl();     // URL de la page suivante
$paginator->previousPageUrl(); // URL de la page précédente
$paginator->url(3);            // URL de la page 3
$paginator->items();           // Tableau des éléments de la page courante

Récapitulatif

  • paginate() — Quand vous avez besoin du total et des liens numérotés (liste standard).
  • simplePaginate() — Quand « Précédent » / « Suivant » suffit (plus rapide).
  • cursorPaginate() — Grands volumes, défilement infini, écritures fréquentes (meilleures performances).
@foreach ($users as $user)
    <p>{{ $user->name }}</p>
@endforeach

{{ $users->links() }}
Passez le résultat de paginate() à la vue et affichez les liens avec links(). La page courante est détectée automatiquement via le paramètre page.
Retourner directement un paginateur produit du JSON automatiquement. Combinez avec une API resource via UserResource::collection($paginator). La réponse contient data (tableau des enregistrements) et diverses métadonnées.
Dernière modification le 13 juillet 2026