paginer avec php et mysql
Transcription
paginer avec php et mysql
PAGINER AVEC PHP ET MYSQL Ymox 29 octobre 2015 Table des matières 1 Introduction 5 2 N’afficher qu’un nombre défini d’éléments 7 2.1 Des solutions peu optimales… . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.1 Le compteur d’itérations . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.2 La requête dans la boucle . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 Une solution optimale avec LIMIT . . . . . . . . . . . . . . . . . . . . . . . . . 3 Générer les liens vers les pages précédente et suivante 3.1 Liens « Suivant » et « Précédent » . . . . . . . . . . . . . . . . . . . . . . . . 4 Afficher des liens vers d’autres pages que les suivante et précédente 4.1 Récupérer le nombre d’éléments total 7 7 8 9 11 11 15 . . . . . . . . . . . . . . . . . . . . . . . 16 4.2 Liste des pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 5 Conclusion 19 3 1 Introduction Jusqu’à présent, toutes les données d’une table s’affichaient sur une seule page. Mais voilà, cela commence à faire beaucoup de données. On doit faire défiler pour trouver l’information, et ça met de plus en plus de temps pour charger une page. Il faut faire quelque chose. Oui, mais quoi ? La solution la plus répandue s’appelle la pagination, soit la séparation en plusieurs pages d’une liste de données. Ce tutoriel, pensé pour ceux qui ne connaissent pas nécessairement tout de MySQL ni de PHP, est là pour vous expliquer plus concrètement le principe, exposer la réflexion pour arriver à une bonne solution, et évidemment vous proposer une implémentation possible. [[information]] | Malgré le fait que du code soit fourni, ce tutoriel nécessite tout de même quelques bases. Vous devriez être déjà capable d’écrire le code de base et de comprendre ce qu’il fait avant d’aller plus loin. Cela implique donc de connaître : — la syntaxe de PHP — les structures de contrôle et les boucles (if, while et for présentes dans ce tutoriel) — la connexion aux bases de données (dans l’idéal avec PDO, utilisé ici, et que je vous recommande chaudement) — le principe et la syntaxe des requêtes SQL avec MySQL (un tutoriel est disponible sur ce site en cas de besoin) 5 2 N’afficher qu’un nombre défini d’éléments Pour commencer, prenons un code simple qui affiche toutes ces données. Il nous servira aussi de base pour continuer le tutoriel, mais je n’en reprendrai que quelques parties au gré des points à observer. <?php $cnx = new PDO('mysql:host=localhost;database=my_database;charset=utf8', 'root', ''); // Partie "Requête" $query = 'SELECT * FROM `my_table`' ; $resultSet = $cnx->query($query); // Partie "Boucle" while ($element = $resultSet->fetch()) { // C'est là qu'on affiche les données } :) Code :Le code de base qui nous servira pour le tutoriel. 2.1 Des solutions peu optimales… Maintenant que nous avons notre code de travail, mettons-nous à l’ouvrage ! :) Il nous a été demandé de n’afficher que 10 éléments à la fois. 2.1.1 Le compteur d’itérations En regardant la ligne 9, on voit de suite qu’on va avoir autant d’itérations qu’il y a de résultats. Du coup, la première solution qui vient à l’esprit, c’est de placer une condition sur le nombre de résultats traités pour sortir de la boucle au bon moment, ce qui nous donnerait ceci : <?php // Partie "Boucle" $limite = 10 ; $compteur = 0 ; while ($element = $resultSet->fetch()) { if ($compteur >= $limite) { break ; } // C'est là qu'on affiche les données :) 7 2 N’afficher qu’un nombre défini d’éléments $compteur++ ; } Code :Mise en place d’une condition sur le nombre d’itérations Cette méthode fonctionne, vous vous frottez les mains d’avoir trouvé une solution rapidement et qui plus est simple à mettre en place. Malheureusement, elle reste consommatrice de mémoire de manière inutile : nous allons récupérer tous les résultats pour n’en utiliser au final que 10. Si ce n’est pas trop gênant quand il n’y a que 20 enregistrements, imaginez quand il y en a au-delà de 8000… 2.1.2 La requête dans la boucle Si le souci est qu’on récupère trop de résultats, nous pouvons alors mettre la requête dans la boucle, ce qui nous donnerait quelque chose dans le genre de ce qui suit : <?php $cnx = new PDO('mysql:host=localhost;database=my_database;charset=utf8', 'root', ''); // Partie "Boucle" $limite = 10 ; for ($i = 0 ; $i < $limite ; $i++) { // Partie "Requête" $query = 'SELECT * FROM `my_table`' ; $resultSet = $cnx->query($query); $element = $resultSet->fetch(); // C'est là qu'on affiche les données } :) Code :Requête dans la boucle [[question]] | Mais… mais… ça nous affiche toujours le même élément � Hé oui, ce qui semblait être une solution logique implique plus de changements que ce qu’on pourrait penser. Il faudrait savoir quel élément a déjà été affiché, et récupérer le premier qui ne l’a pas été. Nous pourrions simplifier, et ne garder que le dernier. Mais il faudrait… -># STOP<Cette solution, outre la complexité qu’elle amène sans en avoir l’air, n’est toujours pas optimale. En plus de la mémoire nécessaire pour garder les résultats à portée de main, il faut aussi penser aux temps d’exécution des requêtes. Un gestionnaire de bases de données est un serveur comme Apache, nginx ou IIS. On lui envoie une requête qu’il traite avant d’envoyer la réponse, que PHP stocke en mémoire. On voit donc qu’il y a au final plus d’un point à prendre en compte : 1. Le temps d’accès au serveur On ne peut pas trop influer dessus, cela dépend de la machine (ou des machines) sur laquelle (ou lesquelles) sont installés PHP et MySQL. 2. Le temps de traitement C’est très lié à la complexité de la requête et à la quantité de résultats, nous pouvons donc en tout cas essayer de demander juste ce qu’il nous faut. Mais ce n’est en général pas le 8 2.2 Une solution optimale avec LIMIT plus gênant, les gestionnaires de bases de données sont suffisamment performants sur ce point. ˆˆ 3. La mémoire pour enregistrer cette réponse Là aussi, essayer de demander uniquement ce que l’on va utiliser permet déjà de faire mieux. Au final, c’est donc le temps d’accès qui pose problème. Ce temps d’accès va rester quasiment identique à chaque requête exécutée, donc il faut trouver un moyen pour le diminuer. Si nous arrivions à mettre en place la seconde proposition, ce temps serait conséquent. La solution est donc de demander directement à MySQL uniquement 10 résultats à la fois. Une seule requête au lieu de 10, donc : — nous n’avons pas 10 fois le temps d’accès qui entre en compte — nous ne surchargeons pas la mémoire de résultats inutiles — nous ne nous cassons plus trop la tête pour savoir lequel est affiché, lequel ne l’est pas :D Néanmoins, il nous manque encore la manière de faire pour dire à MySQL de se limiter à 10 résultats. 2.2 Une solution optimale avec LIMIT Pour ceux qui connaissent un peu MySQL ou qui sont allés chercher des solutions ailleurs, vous connaissez peut-être déjà la clause LIMIT. Et pour ceux qui la découvrent maintenant, on va expliquer ce qu’elle permet. ;) LIMIT : rien que le mot est évocateur. Et il se trouve que c’est bien l’anglais pour « limite ». Il sert donc à limiter le nombre de résultats retournés par une requête — il n’y a d’ailleurs pas que ça, mais nous ne le verrons pas ici. Ce qu’il faut retenir, c’est que nous avons désormais de quoi n’avoir que les 10 enregistrements souhaités. Si nous reprenons la partie “Requête” de notre code de travail de départ et que nous l’adaptons, nous avons ceci : <?php $cnx = new PDO('mysql:host=localhost;database=my_database;charset=utf8', 'root', ''); $limite = 10 ; // Partie "Requête" /* On construit la requête, en remplaçant les valeurs par des marqueurs. Ici, on * n'a qu'une valeur, limite. On place donc un marqueur là où la valeur devrait se * trouver, sans oublier les deux points « : » */ $query = 'SELECT * FROM `my_table` LIMIT :limite' ; /* On prépare la requête à son exécution. Les marqueurs seront identifiés */ $query = $cnx->prepare($query); /* On lie ici une valeur à la requête, soit remplacer de manière sûre un marqueur par * sa valeur, nécessaire pour que la requête fonctionne. */ $query->bindValue( 'limite', // Le marqueur est nommé « limite » $limite, // Il doit prendre la valeur de la variable $limite 9 2 N’afficher qu’un nombre défini d’éléments PDO ::PARAM_INT // Cette valeur est de type entier ); /* Maintenant qu'on a lié la valeur à la requête, on peut l'exécuter pour en récupérer * le résultat */ $resultSet = $query->execute(); // Partie "Boucle" while ($element = $resultSet->fetch()) { // C'est là qu'on affiche les données } :) Code :La solution optimale, qui va nous servir de base pour la suite, avec quelques explications supplémentaires sur les modifications Nous avons désormais un code qui nous affiche juste le nombre souhaité d’éléments, qui est rapide, et qui ne consomme pas trop de mémoire. C’est là un bon début. Ce qu’il nous faut maintenant, c’est de quoi afficher les éléments suivants. Car si nous n’avons plus 10 fois le même, les autres que les 10 premiers doivent être accessibles aussi ! :p *[IIS] :Internet Information Services 10 3 Générer les liens vers les pages précédente et suivante Maintenant que nous savons comment afficher quelques éléments à la fois, nous allons voir comment faire en sorte d’avoir la possibilité de voir toutes les pages, pas seulement la première. Comme auparavant, reprenons une base de départ. <?php $cnx = new PDO('mysql:host=localhost;database=my_database;charset=utf8', 'root', ''); $limite = 10 ; // Partie "Requête" $query = 'SELECT * FROM `my_table` LIMIT :limite' ; $query = $cnx->prepare($query); $query->bindValue('limite', $limite, PDO ::PARAM_INT); $resultSet = $query->execute(); // Partie "Boucle" while ($element = $resultSet->fetch()) { // C'est là qu'on affiche les données } :) Code :Base pour la génération des liens vers les autres pages Et, une fois n’est pas coutume, un peu de réflexion s’impose avant de foncer tête baissée dans le code. Nous savons que l’on aimerait afficher 10 éléments à la fois. Seulement, il nous faut de quoi afficher les 10 suivants, et aussi les 10 précédents, bref, de pouvoir naviguer d’une page à la suivante ou la précédente. 3.1 Liens « Suivant » et « Précédent » Dans un premier temps, nous allons nous contenter de deux simples liens pour passer d’une page à l’autre. Mais d’abord, réfléchissons. Une page, ce sont 10 éléments d’affichés. La page suivante, ce sont les 10 éléments suivants, la page précédente, ce sont les 10 éléments précédents. Il nous faut donc adapter notre requête afin de pouvoir dire, d’une certaine manière, que nous n’avons pas besoin des éléments des pages précédentes. 11 3 Générer les liens vers les pages précédente et suivante Ce moyen est proposé par un autre mot-clé MySQL : OFFSET. Celui-ci s’utilise très simplement de la manière OFFSET :debut. Comme vous pouvez l’avoir déjà compris, ce mot-clé permet de déterminer à partir de quel enregistrement l’on aimerait récupérer les données. D’une certaine manière, c’est donc aussi le nombre d’enregistrements que l’on souhaite “éviter” ou “ignorer”. Je n’ai donc pas choisi les marqueurs sans y réfléchir ! ;) Maintenant, le souci est de calculer cette valeur de début. Regardons de plus près quels sont les enregistrements que nous souhaitons récupérer pour chaque page. Afin de nous y retrouver, nous allons simplement les numéroter, mais pour des questions pratiques, je vais commencer à 0 comme pour les tableaux en PHP. Ce choix se justifie quand nous apprenons que pour MySQL, le premier enregistrement est l’enregistrement 0. Sur la première page, nous récupérons donc les enregistrements 0, 1, 2, 3, …, 9 (vous pouvez compter, ça fait bien 10 :p ). Sur la seconde page, nous allons récupérer les enregistrements 10, 11, 12, …, 19. Sur la troisième : 20, 21, … 29. Vous voyez où je veux en venir ? En fait, sur chaque page, on commence avec l’enregistrement dont la dizaine correspond au numéro de page moins un. Dit autrement, le numéro du premier enregistrement à récupérer pour une page correspond à l’expression ci-dessous : debut = (numeroDeP age − 1) ∗ 10 Il y a juste encore le 10 qui peut gêner, bien qu’on puisse rapidement se rendre compte que ce 10 correspond au nombre d’éléments par page. Ce serait donc intéressant de le remplacer par limite. debut = (numeroDeP age − 1) ∗ limite Nous pouvons désormais adapter la requête pour y ajouter ce que nous avons vu plus haut, du fait que nous savons maintenant comment calculer ce dont elle a besoin pour être utilisable comme on le souhaite. <?php // Partie "Requête" /* On calcule le numéro du premier élément à récupérer */ $debut = ($page - 1) * $limite ; /* La requête contient désormais l'indication de l'élément de départ, * avec le nouveau marqueur … */ $query = 'SELECT * FROM `my_table` LIMIT :limite OFFSET :debut' ; $query = $cnx->prepare($query); $query->bindValue('limite', $limite, PDO ::PARAM_INT); /* … auquel il faut aussi lier la valeur, comme pour la limite */ $query->bindValue('debut', $debut, PDO ::PARAM_INT); /* Le reste se passe comme auparavant */ $resultSet = $query->execute(); Code :Notre nouvelle partie “Requête”, avec les commentaires 12 3.1 Liens « Suivant » et « Précédent » Nous sommes allés un peu trop vite, il nous manque quelque chose, là ! Maintenant que nous avons de quoi calculer le numéro du premier enregistrement pour la page, il nous faut encore savoir sur quelle page on se trouve. Pour ce faire, nous allons rester simple : nous allons passer le numéro de la page souhaitée dans l’URL en utilisant un paramètre de chaîne de requête page. Voilà, nous allons enfin pouvoir passer aux liens vers les pages précédentes et suivantes, en ajoutant simplement la partie “Liens”. ˆˆ <?php $cnx = new PDO('mysql:host=localhost;database=my_database;charset=utf8', 'root', ''); $page = $_GET['page']; $limite = 10; // Partie "Requête" /* On calcule donc le numéro du premier enregistrement */ $debut = ($page - 1) * $limite; /* On ajoute le marqueur pour spécifier le premier enregistrement */ $query = 'SELECT * FROM `my_table` LIMIT :limite OFFSET :debut' ; $query = $cnx->prepare($query); $query->bindValue('limite', $limite, PDO::PARAM_INT); /* On lie aussi la valeur */ $query->bindValue('debut', $debut, PDO::PARAM_INT); $resultSet = $query->execute(); // Partie "Boucle" while ($element = $resultSet->fetch()) { // C'est là qu'on affiche les données } // /* * ?> <a — <a :) Partie "Liens" Notez que les liens ainsi mis vont bien faire rester sur le même script en passant le numéro de page en paramètre */ href="?page=<?php echo $page - 1; ?>">Page précédente</a> href="?page=<?php echo $page + 1; ?>">Page suivante</a> Code :Une pagination effective, mais… Il reste un point à traiter avant que ce script ne soit utilisable. Si l’on arrive dessus sans spécifier de numéro de page, on aura droit à un message comme quoi l’index page n’existe pas. Même si nous savons que l’on doit arriver sur la première page, notre script ne le sait pas ! ˆˆ Il ne manque pas grand-chose pour régler cela, il suffit de modifier un petit peu la récupération du numéro de page comme suit : php linenostart=3 < ?php $page = (!empty($_GET['page']) ? $_GET['page'] : 1); Code :Définition d’un numéro de page par défaut [[attention]] | Le code ainsi fourni n’est pas totalement sécurisé. | | Il vous appartient donc de faire les vérifications d’usage sur ce que contient $_GET['page']. 13 3 Générer les liens vers les pages précédente et suivante Voilà, nous avons maintenant quelque chose de fonctionnel, qu’on va pouvoir continuer d’améliorer. 14 4 Afficher des liens vers d’autres pages que les suivante et précédente Maintenant que nous savons comment afficher quelques éléments à la fois, nous allons voir comment faire en sorte d’avoir la possibilité de savoir combien de pages il y a, et permettre de passer à d’autres pages que celles qui suivent ou précèdent immédiatement. Comme auparavant, reprenons une base de départ. <?php $cnx = new PDO('mysql:host=localhost;database=my_database;charset=utf8', 'root', ''); $page = (!empty($_GET['page']) ? $_GET['page'] : 1); $limite = 10; // Partie "Requête" $debut = ($page - 1) * $limite; $query = 'SELECT * FROM `my_table` LIMIT :limite OFFSET :debut' ; $query = $cnx->prepare($query); $query->bindValue('debut', $debut, PDO::PARAM_INT); $query->bindValue('limite', $limite, PDO::PARAM_INT); $resultSet = $query->execute(); // Partie "Boucle" while ($element = $resultSet->fetch()) { // C'est là qu'on affiche les données } :) // Partie "Liens" ?><a href="?page=<?php echo $page - 1; ?>">Page précédente</a> — <a href="?page=<?php echo $page + 1; ?>">Page suivante</a> Code :Base pour la génération des liens vers les autres pages Comme toujours, un peu de réflexion s’impose avant de foncer tête baissée dans le code. Nous savons que nous aurons 10 éléments par page, mais nous ne savons pas combien de fois cela va faire. Il nous faut donc un moyen de récupérer ce nombre, qui correspondra au nombre de pages total. Un peu de réflexion (ou de recherche ˆˆ ) nous permet de trouver que le nombre de pages total se calcule de la sorte : 15 4 Afficher des liens vers d’autres pages que les suivante et précédente ⌈ nombredElementstotal nombreDeP ages = limite La notation ⌈quelqueChose⌉ se lit comme la fonction plafond de ⌉ quelqueChose, et consiste à prendre l’entier supérieur ou égal à l’argument. Du coup, pour une explication en français : le nombre de pages total est égal à la fonction plafond appliquée sur le résultat de la division du nombre total d’éléments à afficher par le nombre d’éléments par page. Prenez le temps de relire si vous ne comprenez pas. :p Parmi tout ce qu’il y a dans cette expression, il nous manque maintenant nombredElementstotal . 4.1 Récupérer le nombre d’éléments total Nous pourrions commencer par nous dire qu’il nous faut récupérer tous les résultats, puis les compter. Seulement, nous avons déjà vu que récupérer tous les résultats n’est pas une bonne solution quand on n’en affiche que quelques-uns : nous nous retrouvons dans la première situation de la première partie. Il nous faut donc éviter cela. Pour compter un nombre d’enregistrements, il existe la fonction SQL count(). Elle s’utilise sur une colonne, et compte le nombre de valeurs non-nulles qui s’y trouvent. Seulement, si vous arrivez à la combiner avec la version courante de notre requête, vous ne récupérerez plus jamais qu’un seul enregistrement. :-° La vraie solution est d’utiliser un autre mot-clé de MySQL : SQL_CALC_FOUND_ROWS. Ceci, placé juste après SELECT dans une requête, fait en sorte que MySQL conserve une trace du nombre total de résultats trouvés, sans tenir compte de LIMIT : c’est donc exactement ce qu’il nous faut. Pour l’instant, la requête se présente ainsi : SELECT * FROM `my_table` LIMIT :limite OFFSET :debut Code :Requête qui ne fait que récupérer limite enregistrements depuis debut Avec celle-ci, nous récupérons toutes les colonnes des enregistrements. Il nous faut juste ajouter le mot-clé que nous avons vu un peu plus haut, ce qui nous donne la requête suivante : SELECT CALC_FOUND_ROWS * FROM `my_table` LIMIT :limite OFFSET :debut Code :Requête qui récupère limite enregistrements depuis debut et nous compte le nombre total d’éléments Il y a une subtilité avec l’utilisation de CALC_FOUND_ROWS : comme je l’ai mis dans la légende du code, le nombre n’est pas récupéré avec les autres données, il faut le récupérer à part. Cela se fait avec la requête suivante : SELECT found_rows() Code :Récupération du nombre enregistré du fait de CALC_FOUND_ROWS On voit qu’on demande une valeur du fait qu’il s’agit d’une requête commençant par SELECT. found_rows() est une fonction particulière de MySQL qui va justement récupérer ce qui aura été 16 4.2 Liste des pages enregistré lors de la dernière requête SELECT. Je ne vais pas trop m’attarder là-dessus, si vous souhaitez creuser le sujet, je vous conseille la documentation officielle. [[attention]] | Cette dernière requête récupère bien le nombre de résultats totals de la dernière requête SELECT, et pas obligatoirement avec CALC_FOUND_ROWS. Il faut donc éviter de relancer une autre requête entre celle dont vous souhaitez avoir le nombre et la récupération de celui-ci. Au final, notre partie “Requête” va se présenter ainsi : <?php // Partie "Requête" $debut = ($page - 1) * $limite ; /* Ne pas oublier d'adapter notre requête */ $query = 'SELECT CALC_FOUND_ROWS * FROM `my_table` LIMIT :limite OFFSET :debut' ; $query = $cnx->prepare($query); $query->bindValue('debut', $debut, PDO ::PARAM_INT); $query->bindValue('limite', $limite, PDO ::PARAM_INT); $resultSet = $query->execute(); /* Ici on récupère le nombre d'éléments total. Comme c'est une requête, il ne * faut pas oublier qu'on ne récupère pas directement le nombre. * De plus, comme la requête ne contient aucune donnée client pour fonctionner, * on peut l'exécuter ainsi directement */ $resultFoundRows = $cnx->query('SELECT found_rows()'); /* On doit extraire le nombre du jeu de résultat */ $nombredElementsTotal = $resultFoundRows->fetchColumn(); Code :La dernière version de la partie “Requête” Il ne nous reste maintenant plus qu’à afficher les liens vers les autres pages selon leur numéro. 4.2 Liste des pages Nous avons vu une expression mathématique un peu plus haut. Dans un premier temps, nous allons l’adapter en PHP. La fonction qui permet de récupérer l’entier supérieur ou égal le plus proche est la fonction ceil(). Du coup, nous aurons notre calcul qui se présentera ainsi : <?php $nombreDePages = ceil($nombredElementsTotal / $limite); Code :Adaptation de la formule mathématique dans PHP Et maintenant que nous avons toutes les données, il nous faut les afficher. Rien de plus simple qu’une boucle pour ce faire. ;) <?php // Partie "Liens" /* On calcule le nombre de pages */ $nombreDePages = ceil($nombredElementsTotal / $limite); /* Si on est sur la première page, on n'a pas besoin d'afficher de lien 17 4 Afficher des liens vers d’autres pages que les suivante et précédente * vers la précédente. On va donc l'afficher que si on est sur une autre * page que la première */ if ($page > 1): ?><a href="?page=<?php echo $page - 1; ?>">Page précédente</a> — <?php endif; /* On va effectuer une boucle autant de fois que l'on a de pages */ for ($i = 1; $i <= $nombreDePages; $i++): ?><a href="?page=<?php echo $i; ?>"><?php echo $i; ?></a> <?php endfor; /* Avec le nombre total de pages, on peut aussi masquer le lien * vers la page suivante quand on est sur la dernière */ if ($page < $nombreDePages): ?>— <a href="?page=<?php echo $page + 1; ?>">Page suivante</a><?php endif; ?> Code :Génération des liens directs vers chaque page Nous avons désormais notre pagination définitive : nous pouvons accéder à toutes les pages en un clic, nous pouvons passer d’une page à l’autre dans les deux “directions”. Au niveau de l’expérience utilisateur nous avons : — organisé l’information en petites parties qu’il est plus simple pour l’utilisateur de parcourir ; — diminué le temps de chargement des pages ; — réparti un tant soit peu la charge en demandant le plus d’informations possible à MySQL plutôt que de tout faire en PHP ; — minimisé l’empreinte mémoire nécessaire Le jeu en valait donc la chandelle, non ? :) 18 5 Conclusion Pour terminer, je vous remets la base définitive. <?php $cnx = new PDO('mysql:host=localhost;database=my_database;charset=utf8', 'root', ''); $page = (!empty($_GET['page']) ? $_GET['page'] : 1); $limite = 10; // Partie "Requête" $debut = ($page - 1) * $limite; $query = 'SELECT CALC_FOUND_ROWS * AS `total` FROM `my_table` LIMIT :limite OFFSET :deb $query = $cnx->prepare($query); $query->bindValue('debut', $debut, PDO::PARAM_INT); $query->bindValue('limite', $limite, PDO::PARAM_INT); $resultSet = $query->execute(); $resultFoundRows = $cnx->query('SELECT found_rows()'); $nombredElementsTotal = $resultFoundRows->fetchColumn(); // Partie "Boucle" while ($element = $resultSet->fetch()) { // C'est là qu'on affiche les données } :) // Partie "Liens $nombreDePages = ceil($nombredElementsTotal / $limite); if ($page > 1): ?><a href="?page=<?php echo $page - 1; ?>">Page précédente</a> — <?php endif; for ($i = 1; $i <= $nombreDePages; $i++): ?><a href="?page=<?php echo $i; ?>"><?php echo $i; ?></a> <?php endfor; if ($page < $nombreDePages): ?> — <a href="?page=<?php echo $page + 1; ?>">Page suivante</a><?php endif; Code :Une pagination “complète” en PHP et MySQL Comme vous pouvez le constater, cela ne coule pas nécessairement de source, et il y a pas mal de paramètres dont il faut tenir compte. Mais maintenant, vous avez des exemples et donc de bonnes bases pour réaliser vos propres paginations. 19 5 Conclusion Dans un premier temps, vous pouvez vous soucier de l’avertissement donné plus haut : si l’on met dans le paramètre page de l’URL un nombre négatif, à virgule, plus grand que le nombre total de pages, ou même quelque chose qui n’est pas un nombre, ça peut poser problème. Ou alors malgré un nombre respectable d’éléments par page, vous avez trop de pages, et vous souhaitez n’afficher des liens que vers certaines d’entre-elles, comme la première et la dernière, ainsi que les trois précédentes et les trois suivantes… Vous pouvez encore imaginer un système avec de l’AJAX pour charger les éléments suivants comme sur les réseaux sociaux, voire – soyons fous ! — paginer des résultats de recherche en utilisant la fonction http_build_query() à bon escient… Bref, une fois de plus, ce tutoriel vous permet de comprendre la réflexion qu’il y a derrière la pagination afin de pouvoir mettre en place une version très simple, mais néanmoins utilisable et pratique. La suite n’appartient qu’à vous. À vos claviers ! 20