21

Introduction

Ce tutorial montre comment créer une pagination du style Digg en PHP. Il s’inspire du script proposé par StrangerStudios, mais y apporte quelques modifications et quelques corrections.
Nous utiliserons pour cela trois fichiers :

  • paginate.php contient la fonction de pagination.
  • index.php affiche l’ensemble des articles. C’est lui qui appelle la fonction de pagination.
  • style.css est le fichier CSS nécessaire pour la mise en forme des numéros de pages.

Et voici le résultat en image, ou en action sur la page de démo.

Pagination PHP / CSS

Définir la fonction de pagination

La fonction de pagination peut être appelée plusieurs pages différentes. C’est pourquoi il est utile de la définir dans un fichier externe : paginate.php. La fonction prend en paramètres cinq variables expliquées ci-dessous. Mais pour mieux comprendre, prenons un exemple.
Soit http://www.example.com/index.php?cat=2&p=23 l’URL reçue dans le navigateur. Les paramètres à utiliser lors de l’appel de la fonction seront alors :
- $url : ‘http://www.example.com/index.php?cat=2′
- $param : ‘&p=’
- $total : nombre total de pages calculé dans le fichier index.php
- $current : 23
- $adj : par défaut 3, on peut donc ne rien mettre

<?php
/**
 * paginate($url, $param, $total, $current [, $adj]) appelée à chaque affichage de la pagination
 * @param string $url - URL ou nom de la page appelant la fonction, ex: 'index.php' ou 'http://example.com/'
 * @param string $param - paramètre à ajouter à l'URL, ex: '?page=' ou '&amp;p='
 * @param int $total - nombre total de pages
 * @param int $current - numéro de la page courante
 * @param int $adj (facultatif) - nombre de numéros de chaque côté du numéro de la page courante (défaut : 3)
 * @return string $pagination
 */
function paginate($url, $param, $total, $current, $adj=3)
{
	/* Déclaration des variables */
	$prev = $current - 1; // numéro de la page précédente
	$next = $current + 1; // numéro de la page suivante
	$n2l = $total - 1; // numéro de l'avant-dernière page (n2l = next to last)

	/* Initialisation : s'il n'y a pas au moins deux pages, l'affichage reste vide */
	$pagination = '';

	/* Sinon ... */
	if ($total > 1)
	{
		/* Concaténation du <div> d'ouverture à $pagination */
		$pagination .= "<div class=\"pagination\">\n";

		/* ////////// Début affichage du bouton [précédent] ////////// */
		if ($current == 2) // la page courante est la 2, le bouton renvoit donc sur la page 1, remarquez qu'il est inutile de mettre ?p=1
			$pagination .= "<a href=\"{$url}\">◄</a>";
		elseif ($current > 2) // la page courante est supérieure à 2, le bouton renvoit sur la page dont le numéro est immédiatement inférieur
			$pagination .= "<a href=\"{$url}{$param}{$prev}\">◄</a>";
		else // dans tous les autres, cas la page est 1 : désactivation du bouton [précédent]
			$pagination .= '<span class="inactive">◄</span>';
		/* Fin affichage du bouton [précédent] */

		/* ///////////////
		Début affichage des pages, l'exemple reprend le cas de 3 numéros de pages adjacents (par défaut) de chaque côté du numéro courant
		- CAS 1 : il y a au plus 12 pages, insuffisant pour faire une troncature
		- CAS 2 : il y a au moins 13 pages, on effectue la troncature pour afficher 11 numéros de pages au total
		/////////////// */

		/* CAS 1 */
		if ($total < 7 + ($adj * 2))
		{
			/* Ajout de la page 1 : on la traite en dehors de la boucle pour n'avoir que index.php au lieu de index.php?p=1 et ainsi éviter le duplicate content */
			$pagination .= ($current == 1) ? '<span class="active">1</span>' : "<a href=\"{$url}\">1</a>"; // Opérateur ternaire : (condition) ? 'valeur si vrai' : 'valeur si fausse'

			/* Pour les pages restantes on utilise une boucle for */
			for ($i = 2; $i<=$total; $i++)
			{
				if ($i == $current) // Le numéro de la page courante est mis en évidence (cf fichier CSS)
				$pagination .= "<span class=\"active\">{$i}</span>";
				else // Les autres sont affichés normalement
				$pagination .= "<a href=\"{$url}{$param}{$i}\">{$i}</a>";
			}
		}

		/* CAS 2 : au moins 13 pages, troncature */
		else
		{
			/*
			Troncature 1 : on se situe dans la partie proche des premières pages, on tronque donc la fin de la pagination.
			l'affichage sera de neuf numéros de pages à gauche ... deux à droite (cf figure 1)
			*/
			if ($current < 2 + ($adj * 2))
			{
				/* Affichage du numéro de page 1 */
				$pagination .= ($current == 1) ? "<span class=\"active\">1</span>" : "<a href=\"{$url}\">1</a>";

				/* puis des huit autres suivants */
				for ($i = 2; $i < 4 + ($adj * 2); $i++)
				{
				if ($i == $current)
					$pagination .= "<span class=\"active\">{$i}</span>";
					else
					$pagination .= "<a href=\"{$url}{$param}{$i}\">{$i}</a>";
				}

				/* ... pour marquer la troncature */
				$pagination .= ' ... ';

				/* et enfin les deux derniers numéros */
				$pagination .= "<a href=\"{$url}{$param}{$n2l}\">{$n2l}</a>";
				$pagination .= "<a href=\"{$url}{$param}{$total}\">{$total}</a>";
			}

			/*
			Troncature 2 : on se situe dans la partie centrale de notre pagination, on tronque donc le début et la fin de la pagination.
			l'affichage sera deux numéros de pages à gauche ... sept au centre ... deux à droite (cf figure 2)
			*/
			elseif ( (($adj * 2) + 1 < $current) && ($current < $total - ($adj * 2)) )
			{
				/* Affichage des numéros 1 et 2 */
				$pagination .= "<a href=\"{$url}\">1</a>";
				$pagination .= "<a href=\"{$url}{$param}2\">2</a>";

				$pagination .= ' ... ';

				/* les septs du milieu : les trois précédents la page courante, la page courante, puis les trois lui succédant */
				for ($i = $current - $adj; $i <= $current + $adj; $i++)
				{
					if ($i == $current)
					$pagination .= "<span class=\"active\">{$i}</span>";
					else
					$pagination .= "<a href=\"{$url}{$param}{$i}\">{$i}</a>";
				}

				$pagination .= ' ... ';

				/* et les deux derniers numéros */
				$pagination .= "<a href=\"{$url}{$param}{$n2l}\">{$n2l}</a>";
				$pagination .= "<a href=\"{$url}{$param}{$total}\">{$total}</a>";
			}

			/*
			Troncature 3 : on se situe dans la partie de droite, on tronque donc le début de la pagination.
			l'affichage sera deux numéros de pages à gauche ... neuf à droite (cf figure 3)
			*/
			else
			{
				/* Affichage des numéros 1 et 2 */
				$pagination .= "<a href=\"{$url}\">1</a>";
				$pagination .= "<a href=\"{$url}{$param}2\">2</a>";

				$pagination .= ' ... ';

				/* puis des neufs dernières */
				for ($i = $total - (2 + ($adj * 2)); $i <= $total; $i++)
				{
					if ($i == $current)
						$pagination .= "<span class=\"active\">{$i}</span>";
					else
						$pagination .= "<a href=\"{$url}{$param}{$i}\">{$i}</a>";
				}
			}
		}
		/* Fin affichage des pages */

		/* ////////// Début affichage du bouton [suivant] ////////// */
		if ($current == $total)
			$pagination .= "<span class=\"inactive\">►</span>\n";
		else
			$pagination .= "<a href=\"{$url}{$param}{$next}\">►</a>\n";
		/* Fin affichage du bouton [suivant] */

		/* </div> de fermeture */
		$pagination .= "</div>\n";
	}

	/* Fin de la fonction, renvoi de $pagination au programme */
	return ($pagination);
}
?>

Le fichier index.php

La page appelant la fonction de pagination est le fichier index.php avec pour paramètre ‘p’ : index.php?p=
Son utilité? Connexion à la BDD, calcul du nombre total de pages (dépend du nombre d’articles à afficher par page), appel de la fonction de pagination et bien sûr, affichage des articles.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr">
<head>
<title>PHP : pagination</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" type="text/css" media="screen" href="style.css" />
</head>

<body>
<h1>PHP : pagination du style Digg - Démo</h1>
<div id="wrapper">
	<?php
	/* Appel du fichier contenant la fonction pagination() */
	include('paginate.php');

	/* Connexion à la BDD (se place normalement dans un fichier externe) */
	define('DB_HOST', 'localhost');
	define('DB_USER', 'root');
	define('DB_PASSWORD', '');
	define('DB_NAME', 'bdd');

	$dbc = @mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
	if (!$dbc)
	{
		trigger_error('Connexion à la BDD impossible : ' . mysqli_connect_error() );
		exit();
	}

	/* Calcul du nombre total d'entrées $total dans la table posts */
	$res = mysqli_query($dbc, 'SELECT COUNT(*) FROM posts');
	$row = mysqli_fetch_row($res);
	$total = $row[0];

	/* Libération du résultat */
	mysqli_free_result($res);

	/* Déclaration des variables */
	$epp = 3; // nombre d'entrées à afficher par page (entries per page)
	$countp = ceil($total/$epp); // calcul du nombre de pages $countp (on arrondit à l'entier supérieur avec la fonction ceil() )

	/* Récupération du numéro de la page courante depuis l'URL avec la méthode GET */
	if(!isset($_GET['p']) || !is_numeric($_GET['p']) ) // si $_GET['p'] n'existe pas OU $_GET['p'] n'est pas un nombre (petite sécurité supplémentaire)
		$current = 1; // la page courante devient 1
	else
	{
		$page = intval($_GET['p']); // stockage de la valeur entière uniquement
		if ($page < 1) $current=1; // cas où le numéro de page est inférieure 1 : on affecte 1 à la page courante
		elseif ($page > $countp) $current=$countp; //cas où le numéro de page est supérieur au nombre total de pages : on affecte le numéro de la dernière page à la page courante
		else $current=$page; // sinon la page courante est bien celle indiquée dans l'URL
	}

	/* $start est la valeur de départ du LIMIT dans notre requête SQL (est fonction de la page courante) */
	$start = ($current * $epp - $epp);

	/* Récupération des données à afficher pour la page courante */
	$qry = "SELECT p_title FROM posts LIMIT $start, $epp";
	$res = @mysqli_query($dbc, $qry);

	if ($res)
	{
		/* Affichage des données */
		echo "<ul>\n";
		while($item = mysqli_fetch_array($res)) {
			echo "<li>" .$item['p_title']. "</li>\n";
		}
		echo "</ul>\n";

		/* Libération du résultat */
		mysqli_free_result($res);
	}
	else echo mysqli_error($dbc);
	?>

	<?php
	/* Appel de la fonction */
	echo paginate($_SERVER['PHP_SELF'], '?p=', $countp, $current);
	?>
</div>
</body>
</html>

Et enfin le fichier style.css

.pagination {
font:12px Arial, Helvetica, sans-serif;
margin:40px 0;
text-align:center
}

.pagination a {
background:#fff;
border:1px solid #06c;
color:#06c;
margin:2px;
padding:.2em .4em;
text-decoration:none
}

.pagination a:hover {
background:#fff;
border:1px solid #bd88fe;
color:#bd88fe
}

.pagination span.inactive {
background:#fff;
border:1px solid #f0f0ff;
color:#f0f0ff;
margin:2px;
padding:.2em .4em
}

.pagination span.active {
background:#f4ebff;
border:1px solid #bd88fe;
color:#bd88fe;
font-weight:700;
margin:2px;
padding:.2em .4em
}

21 commentaires

1. cloud_strife :

Euuh je pense qu’il a une erreur quelque part, sinon ça doit venir de chez moi, l’erreur:
« Table ’site.posts’ doesn’t exist» 
Alors que ma table se nomme news (et qu’elle existe), le nombre de page se crée mais ça affiche ce message.

05.08.08 - 19:20

2. NotoOn :

Bonjour, dans l’exemple j’utilise une table appelée ‘posts’. Il faut donc modifier les lignes 31 et 57 du fichier index.php et mettre ‘news’ à la place.

05.08.08 - 22:48

3. cloud_strife :

Oups j’ai oublié de changer à la ligne 57, autant pour moi, je ferais plus attention la prochaine fois, sinon je voulais te remercier pour ce tuto, très sympa merci ^^

06.08.08 - 0:05

4. Frakasat :

Merci beaucoup pour cet excellent tutoriel ;)
J’ai pu facilement créer ma fonction de pagination avec celui-ci. =D

Si tu voudrais y jeter un coup d’œil pour voir s’il faut améliorer, ce serait super. Sinon c’est pas important.

Bonne continuation ! =)

19.08.08 - 13:57

5. webtuto :

merci pour le tuto ca a été trèèès utile pour moi

22.08.08 - 1:48

6. coco :

Bonjour!
J’aimerais bien intégrer un style de pagination sur mon site en construction actuellement en html. Je tente d’insérer environ 10 pages ou +. Vue de mes non connaissances en PhP une aide serait favorable afin que je puisse créer l’intégration de ce script pagination. Merci a vous. A bientôt

29.08.08 - 16:50

7. digg-design.fr - le blog! » Archive du blog » Articles les plus votés :

[...] PHP : pagination du style Digg [...]

24.09.08 - 10:39

8. Christophe :

Bonjour,

Avant tout, MERCI, MERCI et MERCI pour le script ! :-)

Juste une petite chose : au niveau de la partie « Début affichage du bouton [précédent]« , il serait peut-être intéressant de rajouter {$param} car il faut peut-être passer des paramètres …

Excellent script en tout cas !

Merci à nouveau !

Christophe

22.10.08 - 7:36

9. Gwen :

Excellent tout simplement

12.03.09 - 15:18

10. rrr :

merci

22.03.09 - 10:23

11. webamies :

vraiment cool rien na dire, je profite au webmaster de mettre un systeme de vote en place pour tirer un chapeau a ses scripts.
merci

28.04.09 - 20:36

12. tino :

je souhaiterais que la pagination soit le résultat d’une recherche. cad si la recherche retourne 20 enregistrements, la pagination se fait dans les 20 enregistrement ainsi de suite, mais je n’y arrive pas. Aider moi svp.

10.05.09 - 17:02

13. stephane :

Super! :D

Récupéré et adapté à mon site, et ça fonctionne très bien.

Une question: à quoi sert le @ dans la fonction « @mysql_query» ?

17.05.09 - 0:58

14. Epoc :

Il sert à ne pas afficher une erreur si elle est rencontrée avec mysql_query :)

27.05.09 - 19:07

15. patrick :

C’est trop cool…
J’ai aimé

10.07.09 - 18:43

16. Popy :

Bonjour,
Merci pour votre système de pagination, il est très bien fait et très simple d’utilisation (surtout pour un débutant comme moi :P ).

Par contre, j’aurais une petite question :

La page 1 affiche les news les plus anciennes tandis que la dernière page affiche les plus récentes. Comment inverser cela ? Faire en sorte que la page 1 affiche les dernières news ?

12.07.09 - 2:32

17. NotoOn :

Bonjour !

Normalement l’ajout d’une clause ORDER BY dans la requête SQL du fichier index.php ligne 57 devrait faire l’affaire. En admettant que postdate soit la date d’un post, vous obtiendrez quelque chose comme ça :
SELECT p_title FROM posts ORDER BY postdate DESC LIMIT $start, $epp
ORDER BY pour ordonner en fonction de l’attribut postdate, et DESC pour préciser qu’on veut décroitre dans les valeurs. On peut mettre ASC pour des valeurs ascendantes :)

14.07.09 - 22:41

18. Marco :

ENFIN le système de pagination ultime !! Que dire si ce n’est un énorme merci !

ps : en plus le design du site est très joli !

21.08.09 - 17:26

19. Skoua :

Thanks for this code, helped me a lot.

02.10.09 - 19:48

20. Raphaël :

Code complet, très bien documenté.

Vraiment nickel =)

12.11.09 - 11:35

21. La Marmotte :

Super procédures
Fonctionnent super bien
Un seul regret :
je n’ai pas su l’adapter pour un fichier comportant 3000 pages.
j’aurai aimé aller de 100 en 100 au milieu
Si quelqu’un peut m’aider ?

merci encore

21.02.10 - 23:48