FONCTION DE PAGINATION INVERSEE OU NON DEPUIS UNE BDD OU UN DOSSIER

cs_Asherah Messages postés 25 Date d'inscription mercredi 14 novembre 2007 Statut Membre Dernière intervention 25 novembre 2007 - 17 nov. 2007 à 17:10
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 - 20 nov. 2007 à 18:01
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/44744-fonction-de-pagination-inversee-ou-non-depuis-une-bdd-ou-un-dossier

neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
20 nov. 2007 à 18:01
kylekatarnls : "La sécurité de ma GET est à 100%, si ce n'est pas un int supérieur à 0, alors elle ne donne rien. De plus le "html perdu" et bien c'est les liens, je vais aps les coder en javascript."

Mouais, ben non... Mais séparer leur affichage du traitement, c'est pas réservé à une élite, c'est à la portée du premier venu et ça simplifie ENORMEMENT la tache du premier gus qui voudra modifier la mise en page. Parce que des liens construits sur plusieurs lignes à partir de morceaux de balises mélangés à des variables php avec des apostrophes, des guillemets échappés qu'on ne sait même plus lequel on doit fermer, excuse moi, mais c'est vraiment pas génial.
Y'a une fonciton toute bête de PHP qui permet de commencer à séparer l'affichage du traitement, c'est la base ; et cette fonction c'est sprintf, qui permet d'y voir quand même sacrément plus clair quand on balance une tripotée de variables dans une chaine.

Et puis tu parles d'une seconde pour 600 pages de 30 entrées... Moi je te parle aussi de charge mémoire et d'optimisation, d'une manière générale.
Essaie de voir un peu plus loin que le bout de ta souris... Quand on utilise une fonction de pagination pour mettre en page les résultats d'un moteur de recherche, ça peut être plusieurs utilisateurs, voire même plusieurs dizaines en même temps. 1 seconde de plus pour chaque personne, je te laisse imaginer la différence que ça fait pour le serveur. C'est pas négligeable. C'est même énorme.

Alors si tu dois charger les 15000 résultats d'une recherche dans une pauvre variable alors que tu vas n'en afficher que 30, je trouve déplorable un tel gâchis de RAM, alors qu'elle pourrait servir à autre chose (comme par exemple stocker plusieurs fois 30 résultats à afficher).

Maintenant si t'as pas du tout envie d'optimiser tes codes et préfères te contenter de piètres performances qui te suffisent, libre à toi. Si tu utilises tes codes pour un site perso avec maxi 3 utilisateurs simultanés, c'est un fait. Mais quand on publie une source, il est normal d'essayer d'optimiser le plus possible. Parce que sinon, la source ne sert à rien.

Maintenant, si tu lis un peu plus attentivement les remarques que j'ai soumises à Asherah et que tu compares sa réaction à la tienne, tu verras qu'il y a une différence.
Elle a reconnu que sa source n'était pas optimisée et elle se penche depuis sur une version orientée objet de sa source. Elle cherche à optimiser et n'a jamais prétendu que sa source était plus performante que la tienne.

Pfffffffffff
kylekatarnls Messages postés 35 Date d'inscription dimanche 4 février 2007 Statut Membre Dernière intervention 1 juillet 2008
20 nov. 2007 à 17:43
La sécurité de ma GET est à 100%, si ce n'est pas un int supérieur à 0, alors elle ne donne rien. De plus le "html perdu" et bien c'est les liens, je vais aps les coder en javascript. Et je ne crois pas que tes 325 lignes non indentées et tes 12 zip soit plus aisés à décrypter.

Je t'ai dit ce que je pensais de ton code (que j'ai noté 5 et pas 0 quand même). Je n'ai présenté cette version que parce que tu me l'a demandé, elle a peut-être l'inconvéniant de prendre une seconde de plus lorsqu'on a 600 pages de 30 entrée BDD, tout comme elle a les avantages de prendre moins de place, de s'adpter à tous les contenus. Je prétends pas que ça vale mieux juste que je préfère utiliser ce code, et j'ai déjà expliqué pourquoi.

PS: Il serait judicieux de remplacer <<, <, >, >> dans les liens par < et > pour éviter des les erreurs de W3C.
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
20 nov. 2007 à 00:28
J'aurais effectivement pu prendre 10 secondes pour lire le code...

if(count($array)>$nepp) //on ne pagine que si c'est nécessaire

C'est bien ce qui me semblait : on stocke dans le tableau TOUT ce qui doit être paginé, même si ça ne doit pas être affiché.
En plus, en faisant comme ça, ça implique une première lecture du résultat mysql, puis une seconde du tableau... Deux boucles sur les mêmes données pour ne les afficher qu'une seule fois... C'est dommage et un vrai gâchi de performances,alors qu'un itérateur permet de ne parcourir qu'une seule fois les données, et uniquement celles que l'on souhaite afficher... Franchement, moins de lignes, c'est un mauvais argument... En plus, pour parcourir un répertoire, là où il faut 10 lignes en procédural, il en faut 3 avec un itérateur (et je ne parle même pas de récursivité, où le nombre de lignes et la charge mémoire augmente de façon phénoménale en procédural, sans bouger augmenter exagérément avec un itérateur).

"$page=$_GET['souspage']*1;//Sous-page évite d'avoir tout à récoder si on utilise déjà une get page"

Mais ça oblige à tout recoder si on utiliser déjà $_GET['souspage']

Bref... Avec un peu de bonne volonté, on peut parvenir à faire du joli code performant... C'est pas difficile, et c'est pas non plus réservé à une élite.
cs_Asherah Messages postés 25 Date d'inscription mercredi 14 novembre 2007 Statut Membre Dernière intervention 25 novembre 2007
19 nov. 2007 à 21:52
Tu ne fait aucunes verifications sur tes transmissions de variable par get, j'aimerais pas l'avoir sur mon site.

Et c'est bete qu'il ne marche pas, car je suis sur que j'aurais pu trouver pleins de trucs du genre. si tu passe a la page 9 alors qu'il n y a que 6 pages tu retrouve dans le vide intersidérale de la bdd.

et puis beaucoup moins de lignes oui, mais sans possibilité de faire un pagination invérsée.

bref, j'aimerais que tu post cette source, voir ce que les autres en pense.

Ils y en a ici qui prennent du temp a faire des reponses constructive.
Et je les remercis beaucoup,et peut etre devrait tu prendre exemple sur eux.

Bon allé sur ce, je n'ajouterais rien, je viens ici pour partager et apprendre.
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
19 nov. 2007 à 20:56
Salut,

@kylekatarnls : J'ai pas regardé ton code en détails, mais si on récupère ce qu'on veut paginer dans un array, excuse moi, mais niveau performances, c'est pas top... Inutile de récupérer TOUTES les données qui sont affichées sur plusieurs pages alors qu'on n'en affiche qu'une partie par page, et qu'on ne peut pas en afficher plus d'une page à chaque fois...
L'itérateur permet de n'itérer que sur une partie, sans surcharger le traitement et la mémoire. En plus, c'est plus joli et plus clair que plein de code avec du html perdu dedans et que personne n'a envie de chercher à décrypter...
kylekatarnls Messages postés 35 Date d'inscription dimanche 4 février 2007 Statut Membre Dernière intervention 1 juillet 2008
19 nov. 2007 à 20:48
Alors la méthode que j'utilise en supposant que l'on est récupéré un contenu SQL ou un dossier dans un array ce qui je pense du coup peut s'adapter à tout :
<?php
$array= array(/*Le contenu à paginer*/);
$nepp=12;//Le nombre d'élément par page
$nbdp=7;//Le nomnbre de numéro de la pagination (impaire plus grand que 3)
if(count($array)>$nepp) //on ne pagine que si c'est nécessaire
{
$array=array_chunk($array,$nepp,true);
$pmax=count($array);
$page=$_GET['souspage']*1;//Sous-page évite d'avoir tout à récoder si on utilise déjà une get page
if(!isset($_GET['souspage']) || !is_int($page) || $page<1 || $page>$pmax)
$page=1;
$array=$array[$page-1];
// On a prélevé le contenu, maintenant on construit la barre des pages
$inter=' ';
if($pmax<=$nbdp)
{
for($i=1;$i<=$pmax;$i++)
if($i==$page)
$inter.='- '.$i.' - ';
else
$inter. ='[?souspage= '.$i.' ['.$i.']] ';
}
else
{
$coun=1;
for($i=min($pmax+1-$nbdp,max(1,ceil($page-(($nbdp-1)/2))));
$i<=min($pmax,max(7,ceil($page+(($nbdp-1)/2))));
$i++) {
if(($i!=1 && $coun==1) || ($i!=$pmax && $coun==$nbdp))
$inter.='... ';
elseif($i==$page)
$inter.='- '.$i.' - ';
else
$inter. ='[?souspage='.$i.' ['.$i.']] ';
$coun++;
}
$inter=' [?souspage='.max(1,$page-7).' <<] [?souspage='.max(1,$page-1).' <]'.$inter.'[?souspage='.min($pmax,$page+-1).' >] [?souspage='.min($pmax,$page+7).' >>] ';
}
}
echo $inter.'
'; // On affiche la pagination au dessus
foreach($array as $key=>$value)
{
//ici on exploite, par exemple on affiche :
echo $value['titre'].'
'.$value['message'].'<hr />';
}
echo $inter.'
'; // On affiche la pagination en dessous
?>
Voilà ça ne prend qu'une petite page c'est totalement paramètrable, qu'y a-t-il besoin de plus ?
cs_Asherah Messages postés 25 Date d'inscription mercredi 14 novembre 2007 Statut Membre Dernière intervention 25 novembre 2007
19 nov. 2007 à 17:05
merci^^
Ca va etre encore mieu lorsque j'aurais fait l'itérateur.^^ patience dans quelques jours ca va froler le codage extraterestre^^
hamidmx Messages postés 3 Date d'inscription mercredi 7 novembre 2007 Statut Membre Dernière intervention 18 juillet 2008
19 nov. 2007 à 15:41
c'est super, surtout après les modifs ...
malalam Messages postés 10839 Date d'inscription lundi 24 février 2003 Statut Membre Dernière intervention 2 mars 2010 25
19 nov. 2007 à 13:20
Ouaip :-) On est bons ;-)
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
19 nov. 2007 à 12:41
Asherah : "avec tout ca, je crois que je vais decouvrir une nouvelle dimension du php^^."

@malalam : Mission accomplie ^^
cs_Asherah Messages postés 25 Date d'inscription mercredi 14 novembre 2007 Statut Membre Dernière intervention 25 novembre 2007
19 nov. 2007 à 12:33
En tout cas elle à l'air genial ta source ^^,
Je vais m'en inspirer si je calle un peu, mais je vais quand meme essayer de faire mon propre itérateur, uniquement dans un but d'apprentissage (si je ne fait pas, je ne saurait pas^^), mais avec tout ca, je crois que je vais decouvrir une nouvelle dimension du php^^.
Merci beaucoup a tous^^.
malalam Messages postés 10839 Date d'inscription lundi 24 février 2003 Statut Membre Dernière intervention 2 mars 2010 25
19 nov. 2007 à 08:23
L'année dernière, j'ai justement fait un package dont les classes peuvent s'utiliser séparément, pour ces traitements, et évidemment, il utilise les itérateurs ;-)
Ca peut te servir de base :
http://www.phpcs.com/codes/PHP5-LIMIT-PACKAGE-LIMITATION-GENERIQUE-JEU-RESULTAT-ITERATEURS_40545.aspx
cs_Asherah Messages postés 25 Date d'inscription mercredi 14 novembre 2007 Statut Membre Dernière intervention 25 novembre 2007
19 nov. 2007 à 01:14
Super!!

j'vais m'intérésser a tout ca, et je vais essayer de l'adapter a la pagination.^^

merci beaucoup^^
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
19 nov. 2007 à 01:11
Ouaip, pardon...

Euh donc je disais ça par spécifiquement pour toi, mais pour ta source. Je donne une piste d'optimisation.

Un itérateur, c'est un objet (instance d'une classe) qui a certaines méthodes (fonctions) définies pour réagir aux fonctions php suivantes :
- current()
- next()
- valid()
- rewind()
- key()

Typiquement, un itérateur est utilisé dans une boucle foreach. La boucle foreach est particulièrement pratique puisqu'elle exécute toute seule ces fonctions sur la variable qui est passée en paramètre.
Lors de l'exécution de cette boucle :
foreach ($aTableau as $sLigne) {
echo $sLigne;
}
foreach effectue tout seul les instructions suivantes :
Initialisation de la boucle :
- rewind($aTableau)
- valid($aTableau)
Une boucle (1 élément) :
- current($aTableau)
- key($aTableau)
- next($aTableau)
- valid($aTableau)
A la fin d'une boucle, si valid() renvoie TRUE, une nouvelle boucle est faite. Sinon, foreach s'arrête.

Un itérateur permet donc de contrôler comment on parcourt quelque chose (un objet, un tableau, n'importe quoi).

Le parcours d'un répertoire peut se faire en 3 lignes. Le parcours récursif d'un répertoire (le répertoire ainsi que tous ses sous-répertoires) idem.
En sacrifiant une ligne supplémentaire de code on peut filtrer les répertoires que l'on affiche (ne pas afficher "." et "..", ne pas afficher les fichiers cachés, ou uniquement ceux qui se terminent par ".truc.php").

Bon là, je commence à rentrer dans le tuto... Alors qu'il en existe déjà : http://www.phpcs.com/tutoriaux/UTILISER-ITERATEURS-PHP5_640.aspx

Concrètement, pour ton cas, un itérateur permettrait de ne parcourir que quelques enregistrements du résultat d'une requête SQL, ou que quelques fichiers d'un répertoire, etc.
Quelques classes permettraient de spécialiser certaines tâches, comme la récupération de ce qu'il faut paginer, et l'affichage de ce quelque chose à paginer. Cela permet d'avoir autant de classes qu'on le souhaite, pour paginer un résultat de requete, des fichiers, le contenu d'un tableau, etc. Sans avoir jamais à modifier le code de l'affichage de la page.

Enfin voilà... Je sais pas si c'était vraiment le meilleur endroit pour tout ça, mais bon...
Si t'y vois pas plus clair, n'hésite pas à poser des questions. J'aime bien les gens curieux, l'étant moi-même : c'est comme ça qu'on progresse.
cs_Asherah Messages postés 25 Date d'inscription mercredi 14 novembre 2007 Statut Membre Dernière intervention 25 novembre 2007
19 nov. 2007 à 00:45
^^
c'est quoi un itérateur? Oo
excuse moi, mais la je suis un peu dans le brouillard.

Et puis tu parle pour moi si je veut faire une pagination?
parceque la, je propose un pagination pour les autres,
et surtout pour les tonnes de petit noobs comme moi qui savent pas ce que c'est un itérateur. +_O

Mais par contre je suis curieuse de savoir, alors si t avais la meme chose en francais.

(oui,j ai pas eu le temp d'apprendre l'anglais a l'ecole :/)

chui preneuse^^
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
18 nov. 2007 à 23:29
Non mais si tu veux vraiment optimiser comme ça, le mieux, c'est encore d'utiliser des itérateurs.
Pour le coup, tu gagnerais non seulement en nombre de lignes, mais aussi en performances, en lisibilité et en facilité de maintenance...
Un peu de lecture : http://www.php.net/~helly/php/ext/spl/

^^
cs_Asherah Messages postés 25 Date d'inscription mercredi 14 novembre 2007 Statut Membre Dernière intervention 25 novembre 2007
18 nov. 2007 à 23:27
Mais je vais rajouter l'option demain.

Je metterais aussi comme pour la pagination bdd les formats invérsé ou non et no_fonction .
cs_Asherah Messages postés 25 Date d'inscription mercredi 14 novembre 2007 Statut Membre Dernière intervention 25 novembre 2007
18 nov. 2007 à 23:22
il ne faut pas enlever la fonction en haut non plus.
cs_Asherah Messages postés 25 Date d'inscription mercredi 14 novembre 2007 Statut Membre Dernière intervention 25 novembre 2007
18 nov. 2007 à 23:13
erff il ne faut pas enlever le "# $total = count($tableau); "
je corrige:

"------------------fonction_pagination.php----------------------

"# function pagination($conteneur_bd_debut, $conteneur_bd_fin, $conteneur_pg_debut, $conteneur_pg_fin, $page, $nbr_par_page, $nbr_liens, $inverse, $champ, $table, $where, $active, $limite_debut, $limite_fin){
# if($active == 'oui'){
# $sql = "SELECT $champ FROM $table WHERE $where BETWEEN $limite_debut AND $limite_fin";
# }
# else{
# $sql = "SELECT $champ FROM $table ORDER BY $where";
# }
# $req = mysql_query($sql) or die('Erreur SQL !
'.$sql.'
'.mysql_error());
# while($data1 = mysql_fetch_assoc($req))
# {
# $tableau[] = $data1[$champ];
# }

--------------------------------------------------------

par:

-------------------------------------------------------
$dossier = 'ton_dossier';
$tableau = array();
if(is_dir($dossier)) {
$dir = opendir($dossier) or die('Impossible d\'ouvrir le dossier');
while (false !($file readdir($dir))) {
if ($file != '.' && $file != '..') {
$tableau[] = $file;
}
}
}
-----------------------------------------------------------
cs_Asherah Messages postés 25 Date d'inscription mercredi 14 novembre 2007 Statut Membre Dernière intervention 25 novembre 2007
18 nov. 2007 à 23:09
Salut Kylekatarnls.

Sache que ma pagination n'est pas faite uniquement pour la BDD, elle est aussi concue pour lire et paginé un dossier tu a juste a remplacé

------------------fonction_pagination.php----------------------

"# function pagination($conteneur_bd_debut, $conteneur_bd_fin, $conteneur_pg_debut, $conteneur_pg_fin, $page, $nbr_par_page, $nbr_liens, $inverse, $champ, $table, $where, $active, $limite_debut, $limite_fin){
# if($active == 'oui'){
# $sql = "SELECT $champ FROM $table WHERE $where BETWEEN $limite_debut AND $limite_fin";
# }
# else{
# $sql = "SELECT $champ FROM $table ORDER BY $where";
# }
# $req = mysql_query($sql) or die('Erreur SQL !
'.$sql.'
'.mysql_error());
# while($data1 = mysql_fetch_assoc($req))
# {
# $tableau[] = $data1[$champ];
# }
# $total = count($tableau); "
-------------------------------------------------------

par:

-------------------------------------------------------
$dossier = 'ton_dossier';
$tableau = array();
if(is_dir($dossier)) {
$dir = opendir($dossier) or die('Impossible d\'ouvrir le dossier');
while (false !($file readdir($dir))) {
if ($file != '.' && $file != '..') {
$tableau[] = $file;
}
}
}
-----------------------------------------------------------

et tu pagine un dossier.

Maintenant si tu a fait plus complexe en beaucoup moins de ligne, je veut bien voir^^.

Et si tu pouvais developpé un peu plus, aussi je suis preneuse.
kylekatarnls Messages postés 35 Date d'inscription dimanche 4 février 2007 Statut Membre Dernière intervention 1 juillet 2008
18 nov. 2007 à 22:33
Je trouve le code un peu long et lourd pour une simple pagination, j'ai déjà fait des paginations plus complexes qui prennent beaucoup moins de lignes de code, de plus toute pagination n'est pas liée à une BDD.

L'idée est bonne mais je pense qu'on peut faire mieux.
cs_Asherah Messages postés 25 Date d'inscription mercredi 14 novembre 2007 Statut Membre Dernière intervention 25 novembre 2007
18 nov. 2007 à 21:10
Salut^^

merci pour tes commentaires.

en fait a la base je n'ai pas fait ce script pour etre une fonction en soit, c'est pour cela qu'il y a en effet beaucoups de parametres, mais je n'ai pas voulus en postant cette source ici qu'elle se limite aux initiés du php, mais je voulais quelle soit accessible meme aux plus novices...
J'ai mis un zip "no_fonction" qui justement, permet de l'exploiter sans ces parametres...

sinon, pour ce qui est du between, tu as raison je vais optimisé la chose ^^.

merci encore^^
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
18 nov. 2007 à 19:23
Salut,

J'ai du mal à comprendre l'intérêt de spécifier en argument les limites inférieure et supérieure des enregistrements à retourner, si ensuite le script recalcule le nombre de pages...

Il m'aurait semble plus rationnel de laisser le script calculer les limites inf. et sup. en fonction de la page demandée et du nombre de pages.

En fait, utiliser BETWEEN pour une pagination n'a de sens que si aucun enregistrement n'a été supprimé.

Si ta table comprends 20 engistrements, que le premier a été supprimé et un autre au milieu, alors les index ont des valeurs allant de 2 à 21. Et pas de 1 à 20. Du coup, le 5ème enregistrement, par exemple, porte l'index 6.
Il faut un autre index, différent de la clé primaire, que tu gères toi-même dans ton script, pour t'assurer qu'aucun ne manque.
Ainsi, lorsque l'on supprime l'enregistrement 5, on décrémente tous les suivants de 1, pour qu'il n'y ait pas de trou. On peut alors utiliser BETWEEN en toute sérénité.

Autre optimisation : tu parcours le résultat et stockes tous les enregistrements dans un tableau associatif, quitte à n'en utiliser qu'une partie ensuite. C'est un peu lourd.
Le plus malin serait de se positionner directement sur le premier enregistrement que l'on souhaite avec mysql_data_seek(), et de boucler autant de fois qu'on veut d'enregistrements. On évite d'avoir un tableau avec plein de trucs inutiles.

Le plus rusé, encore, serait de savoir à l'avance combien d'enregistrements il y a dans la table. Un simple SELECT COUNT(id) FROM table; le dit. Puisqu'on compte un nombre de clés primaires, la requête ne prend pas beaucoup de ressources : mysql est optimisé pour ce genre de requête.
On peut alors tranquillement calculer les premier et dernier enregistrements que l'on souhaite récupérer. La requête ne retournera alors que des enregistrements qui seront utilisés. On n'a alors plus besoin de boucler 2 fois sur le tableau (1 remplissage, 1 lecture). On boucle directement sur le résultat, directement en lecture pour l'afficher.

Trois petites choses encore :
- je déplore que ta fonction demande autant de paramètres
- dommage qu'il n'y ait pas plus de commentaires, parce qu'on a du mal à savoir à quoi sert chaque ligne
- il est fort regrettable que l'affichage html et le traitement des données soient mélangés... On s'y perd, et pour personnaliser le code HTML de sortie, c'est pas évident.
cs_Asherah Messages postés 25 Date d'inscription mercredi 14 novembre 2007 Statut Membre Dernière intervention 25 novembre 2007
18 nov. 2007 à 18:38
Voili! un gros update!

-possibilité d'avoir la pagination invérsée.
-possibilité de restreindre la recherche SQL grace a BETWEEN.

La source est maintenant livrée avec 4 zip.

A savoir la source de base facilement configurable depuis l'index avec la gestion invérsée ou pas pour les novices.

Les versions "allégées" uniquement invérsée ou normal.

Et les version sans l'index, a configurer depuis le script lui meme, pour ceux a qui ca ne fait pas peur. ^^

Voili, et bon paginage^^
cs_Asherah Messages postés 25 Date d'inscription mercredi 14 novembre 2007 Statut Membre Dernière intervention 25 novembre 2007
17 nov. 2007 à 17:10
La présentation graphique est dépouillée a son maximum,
uniquement pour que vous puissiez facilement l'intégrer a votre site avec une mise en forme CSS ou autre.

N'hésitez pas à laisser vos commentaires et pourquoi pas mettre une note^^.
Rejoignez-nous