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