Il arrive souvent que les résultats d'une requête de sélection soient trop nombreux pour être affichés sur une seule page. On met alors en place un système de pagination pour permettre à l'utilisateur de naviguer dans les résultats. Ce système qui permet de passer de page en page peut être de différentes formes et offrir plus ou moins de facilités.

exemple de pagination
exemple de pagination
exemple de pagination

Les bases du traitements sont toujours les mêmes :

- sélection des enregistrements avec mise en réserve du nombre total d'éléments sélectionnés
- affichage d'une partie des résultats
- calcul et affichages des liens de pagination
- l'utilisateurs se déplace dans les pages avec les liens

La fonction mysqli_num_rows() renvoie le nombre d'enregistrements sélectionnés par une requête SELECT.

MySQL facilite le travail de pagination avec la clause LIMIT qui permet non seulement de limiter le nombre d'enregistrements retournés par une requête SELECT, mais aussi de renvoyer des enregistrements à partir d'une certaine position dans un ensemble de résultats.
SELECT * FROM table ORDER BY col LIMIT position, nombre

Le paramètre position est facultatif. Si il est donné, les enregistrements situés avant cette position ne seront pas renvoyés. Le paramètre nombre détermine combien d'enregistrements seront renvoyés par la requête.

Si par exemple, la requête
SELECT * FROM livres ORDER BY liTitre
sélectionne et renvoie tous les enregistrements de la table,
la requête
SELECT * FROM livres ORDER BY liTitre LIMIT 10
renverra uniquement les 10 premiers enregistrements.
La requête
SELECT * FROM livres ORDER BY liTitre LIMIT 5, 10
renverra 10 enregistrements en commençant à la position 5. Dans MySQL la position 5 correspond au sixième enregistrement de l'ensemble des résultats : le premier enregistrement est à la position 0 et non 1.

Position | Contenu de la table | SELECT * FROM table | SELECT * FROM table LIMIT 5 | SELECT * FROM table LIMIT 2, 4
0 | Enreg 1 | + | + |
1 | Enreg 2 | + | + |
2 | Enreg 3 | + | + | +
3 | Enreg 4 | + | + | +
4 | Enreg 5 | + | + | +
5 | Enreg 6 | + |  | +
6 | Enreg 7 | + |  |
7 | Enreg 8 | + |  |
8 | Enreg 9 | + |  |
9 | Enreg 10 | + |  |

L'exemple suivant met en place une pagination par 5 enregistrements dans la liste de livres.

- La requête de sélection est volontairement restreinte aux titres, années et nombres de pages des livres mais pourrait bien sûr intégrer des jointures avec d'autres tables.
- Les liens de pagination sont faits avec des tags a (des "vrais" liens) dont les urls comportent 2 paramètres (t pour le nombre total de livres sélectionnés et p pour la position dans les résultats).
- Ces paramètres sont récupèrés dans le tableau $_GET comme vu dans "Web et PHP", "Les liens".
- Les liens de pagination sont mis dans un tag p avec une classe CSS pagination qui ne fait que centrer le texte à l'affichage.

Exemple : pagination de résultats
<?php ob_start(); require('bib_params.php'); require('bib_fonctions.php'); $pagination = 5; $totalLivres = -1; $position = -1; $nb = 0; //-- Calcul des limites ------------------------------ // Au 1er passage il n'y a pas de paramètres dans l'url // et le tableau $_GET est donc vide. if (isset($_GET['t']) && estEntier($_GET['t'])) { $totalLivres = (int) $_GET['t']; } if (isset($_GET['p']) && estEntier($_GET['p'])) { $position = (int) $_GET['p']; } // Soit 1er passage, soit paramètres GET "modifiés" if ($totalLivres < 0 || $position < 0) { $totalLivres = $position = 0; } // Vérification paramètres GET valides if ($position >= $totalLivres) { $totalLivres = $position = 0; } htmlDebut('Liste des livres', 'bd.css'); $bd = bdConnecter(); //-- Requête ---------------------------------------- $sql = 'SELECT liTitre, liAnnee, liPages FROM livres ORDER BY liTitre'; // Si pas 1er passage : ajoute clause LIMIT if ($totalLivres > 0) { $sql .= " LIMIT $position, $pagination"; } $r = mysqli_query($bd, $sql) or bdErreur($bd, $sql); // 1er passage : récup du nombre total de livres if ($totalLivres == 0) { $totalLivres = mysqli_num_rows($r); } //-- Traitement ------------------------------------- htmlTable(array('Titre', 'Année', 'Pages'), 'tab-bd'); while ($enr = mysqli_fetch_assoc($r)) { htmlProteger($enr); htmlLigne($enr); // Ce test est nécessaire pour la 1ere page $nb ++; if ($nb >= $pagination) { break; } } // Libération de la mémoire associée au résultat de la requête mysqli_free_result($r); echo '</table>'; //-- Affichage pagination --------------------------- echo '<p class="pagination">Pages : '; for ($i = 0, $nb = 0; $i < $totalLivres; $i += $pagination) { $nb ++; if ($i == $position) { // page en cours, pas de lien echo "$nb "; } else { echo '<a href="', $_SERVER['PHP_SELF'], '?t=', $totalLivres, '&p=', $i, '">', $nb, '</a> '; } } echo '</p>'; //-- Déconnexion ------------------------------------ mysqli_close($bd); htmlFin(); ?>

### Pagination de résultats

Reprenez l'exemple précédent de pagination de la liste des livres, mais en utilisant un formulaire pour permettre la navigation dans les pages.

pagination de la liste des livres

- La liste est triée alplhabétiquement sur le titre.
- Il y a 5 livres par page.
- Remarque : on peut avoir autant de bouton submit qu'on veut dans un formulaire.
- Schéma de la base de données

Une solution possible

Exercice : Exercice : pagination avec formulaire
<?php ob_start(); require('bib_params.php'); require('bib_fonctions.php'); $pagination = 5; $totalLivres = -1; $position = -1; $Nb = 0; //-- Calcul des limites ------------------------------ // Au 1er passage il n'y a pas de soumission de // formulaire et le tableau $_POST est donc vide. if (isset($_POST['t']) && estEntier($_POST['t'])) { $totalLivres = (int) $_POST['t']; } if (isset($_POST['btn']) && estEntier($_POST['btn'])) { $position = (int) $_POST['btn']; $position = ($position - 1) * $pagination; } // Soit 1er passage, soit paramètres POST "modifiés" if ($totalLivres < 0 || $position < 0) { $totalLivres = $position = 0; } // Vérification paramètres POST valides if ($position >= $totalLivres) { $totalLivres = $position = 0; } htmlDebut('Liste des livres', 'bd.css'); $bd = bdConnecter(); //-- Requête ---------------------------------------- $sql = 'SELECT liTitre, liAnnee, liPages FROM livres ORDER BY liTitre'; // Si pas 1er passage : ajoute clause LIMIT if ($totalLivres > 0) { $sql .= " LIMIT $position, $pagination"; } $r = mysqli_query($bd, $sql) or bdErreur($bd, $sql); // 1er passage : récup du nombre total de livres if ($totalLivres == 0) { $totalLivres = mysqli_num_rows($r); } //-- Traitement ------------------------------------- htmlTable(array('Titre', 'Année', 'Pages'), 'tab-bd'); while ($enr = mysqli_fetch_assoc($r)) { htmlProteger($enr); htmlLigne($enr); // Ce test est nécessaire pour la 1ere page $nb ++; if ($nb >= $pagination) { break; } } // Libération de la mémoire associée au résultat de la requête mysqli_free_result($r); echo '</table>'; //-- Affichage pagination --------------------------- echo '<form method="POST" ', 'action="', $_SERVER['PHP_SELF'], '">', '<input type="hidden" name="t" ', 'value="', $totalLivres, '">', '<p class="pagination">Pages : '; for ($i = 0, $nb = 0; $i < $totalLivres; $i += $pagination) { $nb ++; if ($i == $position) { // page en cours, pas de lien echo " $nb "; } else { // Les boutons sont des boutons de type submit // qui ont tous le même nom. Ca n'a pas d'importance // car seul celui qui sera cliqué sera transmis avec // le formulaire. Sa valeur permettra de définir à // quel endroit on doit "limiter" le select. echo '<input type="submit" name="btn" ', 'value="', $nb, '">'; } } echo '</p></form>'; //-- Déconnexion ------------------------------------ mysqli_close($bd); htmlFin(); ?>