La pagination est une caractéristique commune dans les applications Web. Presque toutes les applications Laravel sur lesquelles j'ai travaillé ont eu une certaine forme de pagination implémentée.
Mais qu'est-ce que la pagination et pourquoi l'utiliser? Comment pouvons-nous mettre en œuvre une pagination dans nos applications Laravel? Et comment décidons-nous de quelle méthode de pagination utiliser?
Dans cet article, nous allons répondre à ces mêmes questions et explorer comment utiliser la pagination dans Laravel pour les vues de lame et les points de terminaison de l'API. À la fin de cet article, vous devriez vous sentir suffisamment confiant pour commencer à utiliser la pagination dans vos propres projets.
La pagination est une technique utilisée pour diviser un grand ensemble de données en morceaux (ou pages) plus petits. Il vous permet d'afficher un sous-ensemble des données, plutôt que toutes les valeurs possibles à la fois.
Par exemple, imaginez que vous aviez une page qui publie les noms de tous les utilisateurs de votre application. Si vous aviez des milliers d'utilisateurs, il ne serait pas pratique de les afficher tous sur une seule page. Au lieu de cela, vous pouvez utiliser une pagination pour afficher un sous-ensemble des utilisateurs (disons 10 utilisateurs à la fois) sur chaque page et permettre aux utilisateurs de naviguer entre les pages pour afficher plus d'utilisateurs (les 10 suivants).
En utilisant la pagination, vous pouvez:
La pagination peut généralement être divisée en deux types différents:
Laravel fournit trois méthodes différentes pour paginir des requêtes éloquentes dans vos applications:
paginate
- utilise une pagination basée sur le décalage et récupére le nombre total d'enregistrements dans l'ensemble de données. simplePaginate
- utilise une pagination basée sur le décalage mais ne va pas le nombre total d'enregistrements dans l'ensemble de données. cursorPaginate
- utilise une pagination basée sur le curseur et ne va pas récupérer le nombre total d'enregistrements dans l'ensemble de données. Jetons un coup d'œil à chacune de ces méthodes plus en détail.
paginate
Method La méthode paginate
vous permet de récupérer un sous-ensemble de données de la base de données en fonction d'un décalage et d'une limite (nous allons les jeter un œil plus tard lorsque nous examinerons les requêtes SQL sous-jacentes).
Vous pouvez utiliser la méthode paginate
comme ainsi:
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; $users = User::query()->paginate();
Exécuter le code ci-dessus entraînerait la $users
d'être une instance de IlluminateContractsPaginationLengthAwarePaginator
, généralement un objet IlluminatePaginationLengthAwarePaginator
. Cette instance de paginateur contient toutes les informations dont vous avez besoin pour afficher les données paginées dans votre application.
La méthode paginate
peut déterminer automatiquement le numéro de page demandé en fonction du paramètre de requête page
dans l'URL. Par exemple, si vous visitez https://my-app.com/users?page=2
, la méthode paginate
rapporterait la deuxième page des données.
Par défaut, toutes les méthodes de pagination de Laravel par défaut par défaut pour récupérer 15 enregistrements à la fois. Cependant, cela peut être changé à une valeur différente (nous allons voir comment le faire plus tard).
paginate
avec vues de lame Voyons comment utiliser la méthode paginate
lors de la rendu des données dans une vue de lame.
Imaginez que nous avons un itinéraire simple qui récupère les utilisateurs de la base de données dans un format paginé et les transmet à une vue:
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; use Illuminate\Support\Facades\Route; Route::get('users', function () { $users = User::query()->paginate(); return view('users.index', [ 'users' => $users, ]); });
Notre fichier resources/views/users/index.blade.php
peut ressembler à ceci:
<!-- Syntax highlighted by torchlight.dev --><html> <head> <title>Paginate</title> <script src="https://cdn.tailwindcss.com"></script> </head> <body> <div class="max-w-5xl mx-auto py-8"> <h1 class="text-5xl">Paginate</h1> <ul class="py-4"> @foreach ($users as $user) <li class="py-1 border-b">{{ $user->name }}</li> @endforeach </ul> {{ $users->links() }} </div> </body> </html>
La page résultante ressemblerait à ceci:
Décomposons ce qui se passe dans la vue de la lame:
$users
(l'objet IlluminatePaginationLengthAwarePaginator
) et la sortie de son nom. links
sur l'objet $users
. Il s'agit d'une méthode vraiment pratique qui renvoie un HTML qui affiche les liens de pagination (par exemple, "précédent", "suivant" et les numéros de page). Cela signifie que vous n'avez pas à vous soucier de créer vous-même les liens de pagination, et Laravel gérera tout cela pour vous. Nous pouvons également voir que la méthode paginate
nous donne un aperçu des données de pagination. Nous pouvons voir que nous regardons les 16e à 30e records, sur un total de 50 enregistrements. Nous pouvons également voir que nous sommes sur la deuxième page et qu'il y a un total de 4 pages.
Il est important de noter que la méthode links
renverra le style HTML à l'aide de CSS de vent arrière. Si vous souhaitez utiliser autre chose que le vent arrière ou si vous souhaitez styliser les liens de pagination vous-même, vous pouvez consulter la documentation sur la personnalisation des vues de pagination.
paginate
dans les points de terminaison de l'API ainsi que l'utilisation de la méthode paginate
dans les vues de lame, vous pouvez également l'utiliser dans les points de terminaison de l'API. Laravel facilite ce processus en convertissant automatiquement les données paginées en JSON.
Par exemple, nous pourrions créer un point de terminaison /api/users
(en ajoutant la route suivante à notre fichier routes/api.php
) qui renvoie les utilisateurs paginés au format JSON:
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; $users = User::query()->paginate();
L'accès au point de terminaison /api/users
renverrait une réponse JSON similaire à ce qui suit (veuillez noter que j'ai limité le champ data
à seulement 3 enregistrements pour le plaisir de Brivity):
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; use Illuminate\Support\Facades\Route; Route::get('users', function () { $users = User::query()->paginate(); return view('users.index', [ 'users' => $users, ]); });
décomposons la réponse JSON:
current_page
- La page actuelle sur laquelle nous sommes. Dans ce cas, nous sommes sur la première page. data
- Les données réelles elle-même sont renvoyées. Dans ce cas, il contient les 15 premiers utilisateurs (raccourcis à 3 pour Brivity). first_page_url
- L'URL à la première page des données. from
- Le numéro d'enregistrement de départ des données renvoyé. Dans ce cas, c'est le premier record. Si nous étions sur la deuxième page, ce serait 16. last_page
- Le nombre total de pages de données. Dans ce cas, il y a 4 pages. last_page_url
- L'URL de la dernière page des données. links
- Un tableau de liens vers les différentes pages de données. Cela inclut les liens "précédents" et "suivants", ainsi que les numéros de page. next_page_url
- L'URL à la page suivante des données. path
- L'URL de base du point de terminaison. per_page
- Le nombre d'enregistrements étant retourné par page. Dans ce cas, il est 15. prev_page_url
- L'URL à la page précédente des données. Dans ce cas, c'est null
parce que nous sommes sur la première page. Si nous étions sur la deuxième page, ce serait l'URL de la première page. to
- Le numéro d'enregistrement de fin des données en cours de retour. Dans ce cas, c'est le 15e record. Si nous étions sur la deuxième page, ce serait 30. total
- Le nombre total d'enregistrements dans l'ensemble de données. Dans ce cas, il y a 50 enregistrements. L'utilisation de la méthode paginate
dans Laravel Résultats en cours d'exécution de deux requêtes SQL:
Donc, si nous voulions récupérer la première page d'utilisateurs (avec 15 utilisateurs par page), les requêtes SQL suivantes seraient exécutées:
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; $users = User::query()->paginate();
et
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; use Illuminate\Support\Facades\Route; Route::get('users', function () { $users = User::query()->paginate(); return view('users.index', [ 'users' => $users, ]); });
Dans la deuxième requête, nous pouvons voir que la valeur limit
est définie sur 15. Il s'agit du nombre d'enregistrements qui sont retournés par page.
La valeur offset
est calculée comme suit:
<!-- Syntax highlighted by torchlight.dev --><html> <head> <title>Paginate</title> <script src="https://cdn.tailwindcss.com"></script> </head> <body> <div class="max-w-5xl mx-auto py-8"> <h1 class="text-5xl">Paginate</h1> <ul class="py-4"> @foreach ($users as $user) <li class="py-1 border-b">{{ $user->name }}</li> @endforeach </ul> {{ $users->links() }} </div> </body> </html>
Donc, si nous voulions récupérer la troisième page des utilisateurs, la valeur offset
serait calculée comme suit:
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; use Illuminate\Support\Facades\Route; Route::get('paginate', function () { return User::query()->paginate(); });
Par conséquent, la valeur offset
serait de 30 et nous récupérerions les 31 à 45e enregistrements. Les requêtes de la troisième page ressembleraient à:
<!-- Syntax highlighted by torchlight.dev -->{ "current_page": 1, "data": [ { "id": 1, "name": "Andy Runolfsson", "email": "teresa.wiegand@example.net", "email_verified_at": "2024-10-15T23:19:28.000000Z", "created_at": "2024-10-15T23:19:29.000000Z", "updated_at": "2024-10-15T23:19:29.000000Z" }, { "id": 2, "name": "Rafael Cummings", "email": "odessa54@example.org", "email_verified_at": "2024-10-15T23:19:28.000000Z", "created_at": "2024-10-15T23:19:29.000000Z", "updated_at": "2024-10-15T23:19:29.000000Z" }, { "id": 3, "name": "Reynold Lindgren", "email": "juwan.johns@example.net", "email_verified_at": "2024-10-15T23:19:28.000000Z", "created_at": "2024-10-15T23:19:29.000000Z", "updated_at": "2024-10-15T23:19:29.000000Z" } ], "first_page_url": "http://example.com/users?page=1", "from": 1, "last_page": 4, "last_page_url": "http://example.com/users?page=4", "links": [ { "url": null, "label": "« Previous", "active": false }, { "url": "http://example.com/users?page=1", "label": "1", "active": true }, { "url": "http://example.com/users?page=2", "label": "2", "active": false }, { "url": "http://example.com/users?page=3", "label": "3", "active": false }, { "url": "http://example.com/users?page=4", "label": "4", "active": false }, { "url": "http://example.com/users?page=5", "label": "5", "active": false }, { "url": "http://example.com/users?page=2", "label": "Next »", "active": false } ], "next_page_url": "http://example.com/users?page=2", "path": "http://example.com/users", "per_page": 15, "prev_page_url": null, "to": 15, "total": 50 }
et
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; $users = User::query()->paginate();
simplePaginate
Method La méthode simplePaginate
est très similaire à la méthode paginate
mais avec une différence clé. La méthode simplePaginate
ne récupère pas le nombre total d'enregistrements dans l'ensemble de données.
Comme nous venons de le voir, lorsque nous utilisons la méthode paginate
, nous obtenons également des informations sur le nombre total d'enregistrements et de pages disponibles dans l'ensemble de données. Nous pouvons ensuite utiliser ces informations pour afficher des choses comme le nombre total de pages dans l'interface utilisateur ou la réponse API.
Mais si vous n'avez pas l'intention d'afficher ces détails à l'utilisateur (ou développeur consommant l'API), nous pouvons éviter une requête de base de données inutile (qui compte le nombre total d'enregistrements) en utilisant la méthode simplePaginate
.
La méthode simplePaginate
peut être utilisée de la même manière que la méthode paginate
:
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; use Illuminate\Support\Facades\Route; Route::get('users', function () { $users = User::query()->paginate(); return view('users.index', [ 'users' => $users, ]); });
exécuter le code ci-dessus entraînerait la $users
être une instance de IlluminateContractsPaginationPaginator
, généralement un objet IlluminatePaginationPaginator
.
renvoyé par la méthode IlluminatePaginationLengthAwarePaginator
, l'objet paginate
ne contient pas d'informations sur le nombre total d'enregistrements dans l'ensemble de données et n'a aucune idée du nombre de pages ou d'enregistrements totaux. Il connaît simplement la page actuelle des données et s'il y a plus d'enregistrements à récupérer. IlluminatePaginationPaginator
simplePaginate
avec une vue de lame. Nous supposerons que nous avons le même itinéraire qu'auparavant, mais cette fois, nous utilisons la méthode simplePaginate
: simplePaginate
<!-- Syntax highlighted by torchlight.dev --><html> <head> <title>Paginate</title> <script src="https://cdn.tailwindcss.com"></script> </head> <body> <div class="max-w-5xl mx-auto py-8"> <h1 class="text-5xl">Paginate</h1> <ul class="py-4"> @foreach ($users as $user) <li class="py-1 border-b">{{ $user->name }}</li> @endforeach </ul> {{ $users->links() }} </div> </body> </html>
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; use Illuminate\Support\Facades\Route; Route::get('paginate', function () { return User::query()->paginate(); });
est différente de la sortie que nous avons vue lors de l'utilisation de la méthode $users->links()
. Étant donné que la méthode paginate
ne récupère pas le nombre total d'enregistrements, il n'a aucun contexte du nombre total de pages ou d'enregistrements, seulement s'il y a une page suivante ou non. Par conséquent, nous ne voyons que les liens "précédents" et "suivants" dans les liens de pagination. simplePaginate
simplePaginate
dans les points de terminaison de l'API. Laravel convertira automatiquement les données paginées en JSON pour vous. simplePaginate
qui renvoie les utilisateurs paginés au format JSON: /api/users
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; $users = User::query()->paginate();
Lorsque nous aurons atteint cette voie, nous obtiendrons une réponse JSON similaire à ce qui suit (j'ai limité le champ data
à seulement 3 enregistrements pour Brevity):
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; use Illuminate\Support\Facades\Route; Route::get('users', function () { $users = User::query()->paginate(); return view('users.index', [ 'users' => $users, ]); });
Comme nous pouvons le voir, la réponse JSON est très similaire à la réponse que nous avons obtenue lors de l'utilisation de la méthode paginate
. La principale différence est que nous n'avons pas les champs last_page
, last_page_url
, links
ou total
dans la réponse.
Jetons un coup d'œil aux requêtes SQL sous-jacentes qui sont exécutées lorsque vous utilisez la méthode simplePaginate
.
La méthode simplePaginate
repose toujours sur les valeurs limit
et offset
pour récupérer le sous-ensemble de données de la base de données. Cependant, il n'exécute pas la requête pour récupérer le nombre total d'enregistrements dans l'ensemble de données.
La valeur offset
est toujours calculée de la même manière qu'auparavant:
<!-- Syntax highlighted by torchlight.dev --><html> <head> <title>Paginate</title> <script src="https://cdn.tailwindcss.com"></script> </head> <body> <div class="max-w-5xl mx-auto py-8"> <h1 class="text-5xl">Paginate</h1> <ul class="py-4"> @foreach ($users as $user) <li class="py-1 border-b">{{ $user->name }}</li> @endforeach </ul> {{ $users->links() }} </div> </body> </html>
Cependant, la valeur limit
est calculée légèrement différemment de la méthode paginate
. Il est calculé comme:
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; use Illuminate\Support\Facades\Route; Route::get('paginate', function () { return User::query()->paginate(); });
En effet, la méthode simplePaginate
doit récupérer un enregistrement de plus que la valeur perPage
pour déterminer s'il y a plus d'enregistrements à récupérer. Par exemple, disons que nous récupérons 15 enregistrements par page. La valeur limit
serait 16. Donc, si 16 enregistrements devaient être retournés, nous saurions qu'il y a au moins une page de données supplémentaire disponible pour récupérer. Si moins de 16 enregistrements ont été retournés, nous saurions que nous sommes sur la dernière page de données.
Donc, si nous voulions récupérer la première page d'utilisateurs (avec 15 utilisateurs par page), les requêtes SQL suivantes seraient exécutées:
<!-- Syntax highlighted by torchlight.dev -->{ "current_page": 1, "data": [ { "id": 1, "name": "Andy Runolfsson", "email": "teresa.wiegand@example.net", "email_verified_at": "2024-10-15T23:19:28.000000Z", "created_at": "2024-10-15T23:19:29.000000Z", "updated_at": "2024-10-15T23:19:29.000000Z" }, { "id": 2, "name": "Rafael Cummings", "email": "odessa54@example.org", "email_verified_at": "2024-10-15T23:19:28.000000Z", "created_at": "2024-10-15T23:19:29.000000Z", "updated_at": "2024-10-15T23:19:29.000000Z" }, { "id": 3, "name": "Reynold Lindgren", "email": "juwan.johns@example.net", "email_verified_at": "2024-10-15T23:19:28.000000Z", "created_at": "2024-10-15T23:19:29.000000Z", "updated_at": "2024-10-15T23:19:29.000000Z" } ], "first_page_url": "http://example.com/users?page=1", "from": 1, "last_page": 4, "last_page_url": "http://example.com/users?page=4", "links": [ { "url": null, "label": "« Previous", "active": false }, { "url": "http://example.com/users?page=1", "label": "1", "active": true }, { "url": "http://example.com/users?page=2", "label": "2", "active": false }, { "url": "http://example.com/users?page=3", "label": "3", "active": false }, { "url": "http://example.com/users?page=4", "label": "4", "active": false }, { "url": "http://example.com/users?page=5", "label": "5", "active": false }, { "url": "http://example.com/users?page=2", "label": "Next »", "active": false } ], "next_page_url": "http://example.com/users?page=2", "path": "http://example.com/users", "per_page": 15, "prev_page_url": null, "to": 15, "total": 50 }
La requête de la deuxième page aurait l'air:
<!-- Syntax highlighted by torchlight.dev -->select count(*) as aggregate from `users`
cursorPaginate
Method Jusqu'à présent, nous avons examiné les méthodes paginate
et simplePaginate
qui utilisent la pagination basée sur le décalage. Nous allons maintenant jeter un œil à la méthode cursorPaginate
qui utilise la pagination basée sur le curseur.
En tant que tête-à-tête, la pagination basée sur le curseur peut sembler un peu déroutante la première fois que vous le rencontrez. Alors ne vous inquiétez pas si vous ne l'obtenez pas tout de suite. Espérons que, à la fin de cet article, vous comprenez mieux comment cela fonctionne. Je vais également laisser une vidéo impressionnante à la fin de cet article qui explique plus en détail la pagination basée sur le curseur.
Avec une pagination basée sur le décalage, nous utilisons les valeurs limit
et offset
pour récupérer un sous-ensemble de données de la base de données. Nous pouvons donc dire "sauter les 10 premiers enregistrements et récupérer les 10 enregistrements suivants". C'est simple à comprendre et facile à mettre en œuvre. Alors qu'avec la pagination du curseur, nous utilisons un curseur (généralement un identifiant unique pour un enregistrement spécifique dans la base de données) comme point de départ pour récupérer l'ensemble précédent / suivant d'enregistrements.
Par exemple, disons que nous faisons une requête pour récupérer les 15 premiers utilisateurs. Nous supposerons que l'ID du 15e utilisateur est de 20. Lorsque nous voulons récupérer les 15 utilisateurs suivants, nous utiliserons l'ID du 15e utilisateur (20) comme curseur. Nous dirons "Reprendre les 15 utilisateurs suivants avec un ID supérieur à 20".
Vous pouvez parfois voir des curseurs appelés "jetons", "clés", "suivants", "précédents", et ainsi de suite. Ils sont essentiellement une référence à un enregistrement spécifique dans la base de données. Nous examinerons la structure des curseurs plus tard dans cette section lorsque nous examinerons les requêtes SQL sous-jacentes.
Laravel nous permet d'utiliser facilement la pagination basée sur le curseur avec la méthode cursorPaginate
:
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; $users = User::query()->paginate();
Exécuter le code ci-dessus entraînerait le champ $users
étant une instance de IlluminateContractsPaginationCursorPaginator
, généralement un objet IlluminatePaginationCursorPaginator
. Cette instance de paginateur contient toutes les informations dont vous avez besoin pour afficher les données paginées dans votre application.
Semblable à la méthode simplePaginate
, la méthode cursorPaginate
ne récupère pas le nombre total d'enregistrements dans l'ensemble de données. Il ne connaît que la page actuelle des données et s'il y a plus d'enregistrements à récupérer, nous ne sommes donc pas immédiatement conscients du nombre total de pages ou d'enregistrements.
cursorPaginate
avec vues de lame Voyons comment utiliser la méthode cursorPaginate
lors de la création de données dans une vue de lame. Semblable à nos exemples précédents, nous supposerons que nous avons un itinéraire simple qui récupère les utilisateurs de la base de données dans un format paginé et les transmets à une vue:
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; use Illuminate\Support\Facades\Route; Route::get('users', function () { $users = User::query()->paginate(); return view('users.index', [ 'users' => $users, ]); });
La vue de la lame peut ressembler à ceci:
<!-- Syntax highlighted by torchlight.dev --><html> <head> <title>Paginate</title> <script src="https://cdn.tailwindcss.com"></script> </head> <body> <div class="max-w-5xl mx-auto py-8"> <h1 class="text-5xl">Paginate</h1> <ul class="py-4"> @foreach ($users as $user) <li class="py-1 border-b">{{ $user->name }}</li> @endforeach </ul> {{ $users->links() }} </div> </body> </html>
Cela publierait une page similaire à ce qui suit:
Comme nous pouvons le voir, car la méthode cursorPaginate
ne récupère pas le nombre total d'enregistrements dans l'ensemble de données, la sortie de $users->links()
est similaire à la sortie que nous avons vue lors de l'utilisation de la méthode simplePaginate
. Nous ne voyons que les liens "précédents" et "suivants" dans les liens de pagination.
cursorPaginate
dans les points de terminaison de l'API Laravel vous permet également d'utiliser la méthode cursorPaginate
dans les points de terminaison de l'API et convertira automatiquement les données paginées en JSON pour nous.
Créons un point de terminaison /api/users
qui renvoie les utilisateurs paginés au format JSON:
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; use Illuminate\Support\Facades\Route; Route::get('paginate', function () { return User::query()->paginate(); });
Lorsque nous aurons atteint cette voie, nous obtiendrons une réponse JSON similaire à ce qui suit (j'ai limité le champ data
à seulement 3 enregistrements pour Brevity):
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; $users = User::query()->paginate();
Comme nous pouvons le voir, la réponse JSON est similaire aux réponses précédentes que nous avons vues mais avec quelques petites différences. Comme nous ne récupérons pas le nombre total d'enregistrements, nous n'avons pas les champs last_page
, last_page_url
, links
ou total
dans la réponse. Vous avez peut-être aussi remarqué que nous n'avons pas non plus les champs from
et to
.
Au lieu de cela, nous avons les champs next_cursor
et prev_cursor
qui contiennent le curseur pour les pages de données suivantes et précédentes. Puisque nous sommes sur la première page, les champs prev_cursor sont tous les deux prev_page_url
. Cependant, les champs null
et next_cursor
sont définis. next_page_url
est une chaîne codée de base-64 qui contient le curseur pour la page suivante des données. Si nous décodons le champ next_cursor
, nous obtiendrions quelque chose comme ça (embelli pour la lisibilité): next_cursor
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; use Illuminate\Support\Facades\Route; Route::get('users', function () { $users = User::query()->paginate(); return view('users.index', [ 'users' => $users, ]); });
users.id
_pointsToNextItems
, cela signifie que le curseur doit être utilisé pour récupérer l'ensemble suivant d'enregistrements avec un ID supérieur à la valeur true
. Si la valeur est users.id
, cela signifie que le curseur doit être utilisé pour récupérer l'ensemble des enregistrements précédents avec un ID inférieur à la valeur false
. users.id
<!-- Syntax highlighted by torchlight.dev --><html> <head> <title>Paginate</title> <script src="https://cdn.tailwindcss.com"></script> </head> <body> <div class="max-w-5xl mx-auto py-8"> <h1 class="text-5xl">Paginate</h1> <ul class="py-4"> @foreach ($users as $user) <li class="py-1 border-b">{{ $user->name }}</li> @endforeach </ul> {{ $users->links() }} </div> </body> </html>
et prev_cursor
sont maintenant définis, et les champs prev_page_url
et next_cursor
ont été mis à jour avec le curseur pour la page suivante des données. next_page_url
. cursorPaginate
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; use Illuminate\Support\Facades\Route; Route::get('paginate', function () { return User::query()->paginate(); });
et les commandons par la colonne users
dans l'ordre croissant. Semblable à la méthode id
, nous récupérons 16 lignes parce que nous voulons déterminer s'il y a plus d'enregistrements à récupérer. simplePaginate
<!-- Syntax highlighted by torchlight.dev -->{ "current_page": 1, "data": [ { "id": 1, "name": "Andy Runolfsson", "email": "teresa.wiegand@example.net", "email_verified_at": "2024-10-15T23:19:28.000000Z", "created_at": "2024-10-15T23:19:29.000000Z", "updated_at": "2024-10-15T23:19:29.000000Z" }, { "id": 2, "name": "Rafael Cummings", "email": "odessa54@example.org", "email_verified_at": "2024-10-15T23:19:28.000000Z", "created_at": "2024-10-15T23:19:29.000000Z", "updated_at": "2024-10-15T23:19:29.000000Z" }, { "id": 3, "name": "Reynold Lindgren", "email": "juwan.johns@example.net", "email_verified_at": "2024-10-15T23:19:28.000000Z", "created_at": "2024-10-15T23:19:29.000000Z", "updated_at": "2024-10-15T23:19:29.000000Z" } ], "first_page_url": "http://example.com/users?page=1", "from": 1, "last_page": 4, "last_page_url": "http://example.com/users?page=4", "links": [ { "url": null, "label": "« Previous", "active": false }, { "url": "http://example.com/users?page=1", "label": "1", "active": true }, { "url": "http://example.com/users?page=2", "label": "2", "active": false }, { "url": "http://example.com/users?page=3", "label": "3", "active": false }, { "url": "http://example.com/users?page=4", "label": "4", "active": false }, { "url": "http://example.com/users?page=5", "label": "5", "active": false }, { "url": "http://example.com/users?page=2", "label": "Next »", "active": false } ], "next_page_url": "http://example.com/users?page=2", "path": "http://example.com/users", "per_page": 15, "prev_page_url": null, "to": 15, "total": 50 }
<!-- Syntax highlighted by torchlight.dev -->select count(*) as aggregate from `users`
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; $users = User::query()->paginate();
Comme nous pouvons le voir, nous récupérons les 16 enregistrements suivants du tableau users
qui ont un id
supérieur à 15 (depuis 15 était le dernier identifiant de la page précédente).
Supposons maintenant que l'ID du premier utilisateur à la page 2 est 16. Lorsque nous retournons à la première page des données de la deuxième page, le curseur suivant serait utilisé:
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; use Illuminate\Support\Facades\Route; Route::get('users', function () { $users = User::query()->paginate(); return view('users.index', [ 'users' => $users, ]); });
Lorsque cela est décodé, nous obtenons l'objet JSON suivant:
<!-- Syntax highlighted by torchlight.dev --><html> <head> <title>Paginate</title> <script src="https://cdn.tailwindcss.com"></script> </head> <body> <div class="max-w-5xl mx-auto py-8"> <h1 class="text-5xl">Paginate</h1> <ul class="py-4"> @foreach ($users as $user) <li class="py-1 border-b">{{ $user->name }}</li> @endforeach </ul> {{ $users->links() }} </div> </body> </html>
Lorsque nous passons à la page suivante des résultats, le dernier enregistrement récupéré est utilisé comme curseur. Lorsque nous retournons à la page précédente des résultats, le premier enregistrement récupéré est utilisé comme curseur. Pour cette raison, nous pouvons voir que la valeur users.id
est définie sur 16 dans le curseur. Nous pouvons également voir que la valeur _pointsToNextItems
est définie sur false
parce que nous revenons à l'ensemble des éléments précédents.
En conséquence, la requête SQL suivante serait exécutée pour récupérer l'ensemble des enregistrements précédents:
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; use Illuminate\Support\Facades\Route; Route::get('paginate', function () { return User::query()->paginate(); });
Comme nous pouvons le voir, la contrainte where
vérifie maintenant les enregistrements avec un id
moins de 16 (puisque 16 a été le premier ID à la page 2) et les résultats sont commandés par ordre décroissant.
Jusqu'à présent, dans nos exemples d'API, nous venons de renvoyer les données paginées directement du contrôleur. Cependant, dans une application du monde réel, vous voudrez probablement traiter les données avant de les renvoyer à l'utilisateur. Cela pourrait être quelque chose de l'ajout ou de la suppression des champs, de la conversion des types de données ou même de la transformation des données en un format différent. Pour cette raison, vous voudrez probablement utiliser les ressources API car ils vous fournissent un moyen de transformer systématiquement vos données avant de les renvoyer.
Laravel vous permet d'utiliser des ressources API aux côtés de la pagination. Regardons un exemple de la façon de procéder.
Imaginez que nous avons créé une classe de ressources API AppHttpResourcesUserResource
qui transforme les données de l'utilisateur avant de les renvoyer. Cela pourrait ressembler à ceci:
<!-- Syntax highlighted by torchlight.dev -->{ "current_page": 1, "data": [ { "id": 1, "name": "Andy Runolfsson", "email": "teresa.wiegand@example.net", "email_verified_at": "2024-10-15T23:19:28.000000Z", "created_at": "2024-10-15T23:19:29.000000Z", "updated_at": "2024-10-15T23:19:29.000000Z" }, { "id": 2, "name": "Rafael Cummings", "email": "odessa54@example.org", "email_verified_at": "2024-10-15T23:19:28.000000Z", "created_at": "2024-10-15T23:19:29.000000Z", "updated_at": "2024-10-15T23:19:29.000000Z" }, { "id": 3, "name": "Reynold Lindgren", "email": "juwan.johns@example.net", "email_verified_at": "2024-10-15T23:19:28.000000Z", "created_at": "2024-10-15T23:19:29.000000Z", "updated_at": "2024-10-15T23:19:29.000000Z" } ], "first_page_url": "http://example.com/users?page=1", "from": 1, "last_page": 4, "last_page_url": "http://example.com/users?page=4", "links": [ { "url": null, "label": "« Previous", "active": false }, { "url": "http://example.com/users?page=1", "label": "1", "active": true }, { "url": "http://example.com/users?page=2", "label": "2", "active": false }, { "url": "http://example.com/users?page=3", "label": "3", "active": false }, { "url": "http://example.com/users?page=4", "label": "4", "active": false }, { "url": "http://example.com/users?page=5", "label": "5", "active": false }, { "url": "http://example.com/users?page=2", "label": "Next »", "active": false } ], "next_page_url": "http://example.com/users?page=2", "path": "http://example.com/users", "per_page": 15, "prev_page_url": null, "to": 15, "total": 50 }
Dans la méthode toArray
, nous définissons que chaque fois que nous traitons un utilisateur via cette ressource, nous voulons seulement retourner les champs id
, name
et email
.
Maintenant, créons un point de terminaison simple /api/users
API dans notre fichier routes/api.php
qui renvoie les utilisateurs paginés à l'aide du AppHttpResourcesUserResource
:
<!-- Syntax highlighted by torchlight.dev -->select count(*) as aggregate from `users`
Dans le code ci-dessus, nous récupérons une seule page d'utilisateurs (supposons que c'est la première page contenant 15 utilisateurs) de la base de données. Nous passons ensuite le champ $users
(qui sera une instance de IlluminatePaginationLengthAwarePaginator
) à la méthode UserResource::collection
. Cette méthode transformera les données paginées à l'aide du AppHttpResourcesUserResource
avant de les renvoyer à l'utilisateur.
Lorsque nous atteindrons le point final /api/users
, nous obtiendrons une réponse JSON similaire à ce qui suit (j'ai limité le champ data
à seulement 3 enregistrements pour la concitation):
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; $users = User::query()->paginate();
Comme nous pouvons le voir dans le JSON ci-dessus, Laravel détecte que nous travaillons avec un ensemble de données paginé et renvoie les données paginées dans un format similaire qu'auparavant. Cependant, cette fois, les utilisateurs du champ data
ne contiennent que les champs id
, name
et email
que nous avons spécifiés dans notre classe de ressources API. D'autres champs (current_page
, from
, last_page
, links
, path
, per_page
, to
, et total
) sont toujours retournés car ils font partie des données paginées, mais elles ont été placées à l'intérieur d'un champ meta
. Il y a aussi un champ links
qui contient les liens first
, last
, prev
et next
vers les différentes pages de données.
Lors de la création de vues avec des données paginées, vous souhaiterez peut-être permettre à l'utilisateur de modifier le nombre d'enregistrements affichés par page. Cela peut être via une liste déroulante ou un champ de saisie du nombre.
Laravel facilite la modification du nombre d'enregistrements affichés par page en passant un paramètre perPage
aux méthodes simplePaginate
, paginate
et cursorPaginate
. Ce paramètre vous permet de spécifier le nombre d'enregistrements que vous souhaitez afficher par page.
Jetons un coup d'œil à un exemple simple de la façon de lire un paramètre de requête per_page
et d'utiliser ceci pour modifier le nombre d'enregistrements récupérés par page:
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; use Illuminate\Support\Facades\Route; Route::get('users', function () { $users = User::query()->paginate(); return view('users.index', [ 'users' => $users, ]); });
Dans l'exemple ci-dessus, nous saisissons la valeur du paramètre de requête per_page
. Si la valeur n'est pas fournie, nous allons par défaut 10. Nous passons ensuite cette valeur au paramètre perPage
de la méthode paginate
.
Nous pourrions ensuite accéder à ces différentes URL:
https://my-app.com/users
- Affichez la première page d'utilisateurs avec 10 enregistrements par page. https://my-app.com/users?per_page=5
- Affichez la première page des utilisateurs avec 5 enregistrements par page. https://my-app.com/users?per_page=5&page=2
- Affichez la deuxième page des utilisateurs avec 5 enregistrements par page. Maintenant que nous avons examiné les différents types de pagination et comment les utiliser dans Laravel, nous discuterons de la façon de décider laquelle de ces approches utiliser dans votre application.
Si vous construisez un point de terminaison d'interface utilisateur ou d'API qui nécessite que le nombre total d'enregistrements ou de pages soit affiché, alors la méthode paginate
est probablement un choix judicieux.
Si vous n'avez pas besoin de ces éléments, alors les simplePaginate
ou cursorPaginate
seront plus efficaces car ils n'effectuent pas des requêtes inutiles pour compter le nombre total d'enregistrements.
Si vous avez besoin de pouvoir passer à une page spécifique de données, la pagination basée sur le décalage est plus appropriée. Étant donné que la pagination du curseur est avec état, elle s'appuie sur la page précédente pour savoir où aller ensuite. Il n'est donc pas aussi facile de passer à une page spécifique.
Alors que lorsque vous utilisez une pagination de décalage, vous pouvez généralement passer le numéro de page dans la demande (peut-être comme paramètre de requête) et sauter sur cette page sans avoir aucun contexte de la page précédente.
En raison de la façon dont les bases de données gèrent les valeurs offset
, la pagination basée sur le décalage devient moins efficace à mesure que le numéro de page augmente. En effet, lorsque vous utilisez un décalage, la base de données doit toujours parcourir tous les enregistrements jusqu'à la valeur de décalage. Ils sont juste jetés et ne sont pas retournés dans les résultats de la requête.
Voici un excellent article qui explique cela plus en détail: https://use-the-index-luke.com/no-offset.
Ainsi, à mesure que la quantité totale de données dans la base de données augmente et que le nombre de pages augmente, la pagination basée sur le décalage peut devenir moins efficace. Dans ces cas, la pagination basée sur le curseur est plus performante, surtout si le champ de curseur est indexé, car les enregistrements précédents ne sont pas lus. Pour cette raison, si vous allez utiliser une pagination contre un grand ensemble de données, vous voudrez peut-être opter pour la pagination du curseur sur la pagination de décalage.
La pagination basée sur le décalage peut souffrir de problèmes si l'ensemble de données sous-jacent change entre les demandes.
Jetons un coup d'œil à un exemple.
Disons que nous avons les 10 utilisateurs suivants dans notre base de données:
Nous faisons une demande pour récupérer la première page (contenant 5 utilisateurs) et obtenir les utilisateurs suivants:
Lorsque nous naviguons vers la page 2, nous nous attendons à obtenir des utilisateurs de 6 à 10. Cependant, imaginons qu'avant de charger la page 2 (pendant que nous consulons toujours la page 1), l'utilisateur 1 est supprimé de la base de données. Étant donné que la taille de la page est de 5, la requête pour récupérer la page suivante ressemblerait à ceci:
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; $users = User::query()->paginate();
Cela signifie que nous sautons les 5 premiers enregistrements et récupérons les 5.
suivantsCela entraînerait la page 2 contenant les utilisateurs suivants:
Comme nous pouvons le voir, l'utilisateur 6 manque dans la liste. En effet
La pagination basée sur le curseur n'a pas ce problème, car nous ne sautons pas les enregistrements, nous allons simplement récupérer le prochain ensemble d'enregistrements basé sur un curseur. Imaginons que nous avions utilisé une pagination basée sur le curseur dans l'exemple ci-dessus. Le curseur de la page 2 serait l'ID de l'utilisateur 5 (que nous supposerons est 5) car il s'agissait du dernier enregistrement de la première page. Ainsi, notre requête pour la page 2 peut ressembler à ceci:
<!-- Syntax highlighted by torchlight.dev -->use App\Models\User; $users = User::query()->paginate();
exécuter la requête ci-dessus renverrait les utilisateurs 6 à 10 comme prévu.
Cela devrait, espérons-le, mettre en évidence la façon dont la pagination basée sur le décalage peut devenir problématique lorsque les données sous-jacentes sont modifiées, ajoutées ou supprimées pendant la lecture. Il devient moins prévisible et peut conduire à des résultats inattendus.
Il est important de se rappeler que vous n'êtes pas fixé à l'utilisation d'un seul type de pagination dans votre application. Dans certains endroits, la pagination de compensation pourrait être plus appropriée (peut-être à des fins d'interface utilisateur) et dans d'autres, la pagination du curseur pourrait être plus efficace (comme lorsque vous travaillez avec un grand ensemble de données). Vous pouvez donc mélanger et assortir des méthodes de pagination dans votre application en fonction du cas d'utilisation.
Cependant, si vous construisez une API, je vous recommande vivement que vous soyez cohérent et utilisez une seule approche de pagination pour tous vos points de terminaison. Cela permettra aux développeurs de comprendre comment utiliser votre API et d'éviter toute confusion.
Vous ne voulez pas qu'ils aient à se rappeler quels points de terminaison utilisent la pagination décalée et lesquelles utilisent la curseur-pagination.
Bien sûr, ce n'est pas une règle stricte et rapide. Si vous avez vraiment besoin d'utiliser une méthode de pagination différente dans un point de terminaison particulier, allez-y. Mais assurez-vous simplement de le comprendre dans la documentation pour faciliter la compréhension des développeurs.
Si vous êtes plus un apprenant visuel, vous voudrez peut-être consulter cette vidéo impressionnante d'Aaron Francis qui explique la différence entre la pagination offset et basée sur le curseur plus en détail:
Dans cet article, nous avons examiné les différents types de pagination à Laravel et comment les utiliser. Nous avons également examiné leurs requêtes SQL sous-jacentes et comment décider quelle méthode de pagination utiliser dans votre application.
J'espère que vous devriez maintenant vous sentir plus confiant dans l'utilisation de la pagination dans vos applications Laravel.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!