SPIP - Contrib

[ar] [en] [es] [fr] [it]



Accueil du site > Navigation > Tris hétérogénes > Boucles sans tables

Boucler sur un tableau, un compteur, les langues, une condition ...

vendredi 20 janvier 2006, par piif. Dernier ajout vendredi 13 juillet 2007


construction de boucles permettant de parcourir un tableau php, d’itérer sur un compteur, de faire une condition, et d’itérer sur les langues


Nota SPIP-Contrib : cette contribution existe dorénavant en plugin. En attendant la mise à jour de sa documentation cet article reste celui de référence
- sur la Zone : http://trac.rezo.net/trac/spip-zone...

Mise à jour du 29/11/2005 : quelques debugs et améliorations

L’idée de ce package est de créer des boucles sur autre chose que des tables sql. Il y a 4 exemples dans le zip :

  • boucle langues : permet d’itérer sur les langues spip
  • boucle for : permet de boucler en affectant #COMPTEUR_BOUCLE à des valeurs successives (le for de php en gros)
  • boucle tableau : permet d’itérer sur les clés/valeurs d’un tableau
  • boucle if : permet de faire un tour de boucle si une condition est vraie

Installation

le tar contient 9 fichiers : 4 .php à inclure depuis mes_fonctions ou assimilé, 4 squelettes d’exemple et un mes_fonctions d’exemple. il suffit de détarrer ça à la racine d’un spip 1.8.2 ou 1.9, de mettre un include des fichiers php dans mes_fonctions.php3 (voir le fichier mes_fonctions.exemple.php3) et d’appeler les exemples par des urls comme page.php?fond=testLangue

Attention : le code est relativement quick and dirty. Je ne sais pas trop ce que ça donne si on utilise ces boucles avec des trucs tordus, des critères de travers ou des imbrications à tiroirs.

La boucle langue

C’est la plus simple car elle n’accepte aucun critère (on verra plus tard si y’a besoin).

Elle permet d’itérer sur les langues définies "actives" dans l’interface privée, et peut servir, par exemple, à faire un menu langue personnalisé (tout ça viens d’une discussion sur la liste ).

L’exemple joint (testLangue.html) montre à peu près tout ce qu’on peut faire avec, c’est à dire pas grand chose :-)

<BOUCLE_l(LANGUES)>
<br>#LANGUE : [(#LANGUE|traduire_nom_langue)].
</BOUCLE_l>

Si on ajoute le critère {tout} la boucle prend toutes les langues définies, et pas seulement celles activées. Attention, si la configuration de précise pas de langues, elles sont toutes considérées actives.

Comment ça marche : il suffit de définir une fonction boucle_LANGUES pour traiter ce type de boucle.
Comme il n’y a pas de table ’langues’, on ’truande’ le compilo en définissant des entrées dans la variable ’tables_principales’, et en spécifiant sa pseudo-existance dans table_des_tables.
Dans la fonction, il faut maintenant définir tout le code à générer pour la boucle. À coups de copier/coller depuis les boucles existant dans spip, on en déduit le bout de code qui va bien.

Pour le critère tout, c’est leparseur qui se débrouille et fixe le champ "tout" à "true". Selon cette valeur, on va chercher un meta ou l’autre.

La boucle for

Celle là est un peu tordue, mais elle permet de voir comment utiliser des critères. L’utilisation basique de cette boucle est la suivante :

<BOUCLE_f(FOR){debut=2}{fin=10}{par 2}>
<br>#COMPTEUR_BOUCLE
</BOUCLE_f>

Contrairement à une boucle for dans php, on n’a pas besoin de définir de variable d’itération puisque #COMPTEUR_BOUCLE joue ce rôle.
Attention cependant, dans cet exemple, à la fin #TOTAL_BOUCLE vaut 10 (dernière valeur) et pas 5 (nombre de tours).

Si le critère "par" est inversé ({!par 2}), on va en descendant, de "fin " jusqu’à "debut".

Comment ça marche : comme pour la boucle précédente, on défini une pseudo table qui contient des champs debut, fin pour que le phraseur ne râle pas quand il croise ces noms dans les critères.

Là ou ça se complique, c’est qu’il faut analyser les critères pour récupérer la valeur de debut, fin. Pour ça, on parcours le tableau criteres de la structure de boucle et on cherche les entrées ayant ’=’ comme opérateur et l’une des trois valeurs comme partie gauche.
Il suffit alors d’appeler la fonction calculer_liste sur la partie droite pour récupérer le code à insérer dans le code de la boucle. Ce qui est magique, c’est que ça marche tout seul pour un debut=1 ou pour un debut=[(#TOTO|machin{bidule})].5

Une tambouille à base de eval permettrait également de traiter un debut=#TOTO+1, mais j’ai l’impression que ça complique pas mal les choses, et je ne maîtrise pas les effets de bord.

Enfin, pour le critère "par", c’est une fonction critere_par qui surcharge la fonction par défaut et la rappelle si on n’est pas en train de traiter une boucle for.

La boucle tableau

Après le for, le foreach ! Il s’agit cette fois d’aller chercher un tableau associatif php et d’itérer sur ses clés / valeurs. Le tableau doit sortir d’un élément extérieur (via mes_fonctions ou autre) ou venir de spip (table meta, tableau des tables et autres structures internes).

Il y a 2 contraintes :

  • il faut que ce tableau soit global, puisque quand on interprète une boucle, c’est toujours depuis une fonction gérée par le compilo
  • on ne peut pas définir ce tableau dans un tag php du squelette puisque celui ci sera évalué après le code du squelette (par contre ça marcherait sûrement dans le tag évoqué sur la liste ).

L’utilisation de base est la suivante :

<BOUCLE_t(TABLEAU){var=toto}>
<br>- #CLE => #VALEUR
</BOUCLE_t>
vide ...
<//B_t>

Le critère var doit contenir le nom de la variable, et les balises CLE et VALEUR correspondent aux paires clé/valeur. Si le tableau est vide ou n’existe pas, on n’entre pas dans la boucle et la partie <//B_..> est donc prise à la place.

Si jamais la valeur d’une entrée de tableau est elle même un tableau, on peut utiliser cette valeur comme variable d’une boucle imbriquée via un critère {valeur} (exactement comme quand on utilise un critère id_rubrique pour lister les articles d’une rubrique sortie d’une boucle englobante) :

<BOUCLE_t(TABLEAU){var=tables_principales}>
        <BR>- #CLE => #VALEUR
        <BOUCLE_tt(TABLEAU){valeur}>
                <BR>-- #CLE => #VALEUR
                <BOUCLE_ttt(TABLEAU){valeur}>
                        <BR>--- #CLE => #VALEUR
                </BOUCLE_ttt>
        </BOUCLE_tt>
</BOUCLE_t>

Ici, on sort les entrée de la table tables_principales dont chaque valeur est elle-même un tableau de tableaux.

Pour aller chercher un élément de tableau directement, on peut utiliser le critère cle, éventuellement en plusieurs exemplaires :

<BOUCLE_t(TABLEAU){var=tables_principales}{cle=spip_tableau}{cle=field}>
<br>- #CLE => #VALEUR
</BOUCLE_t>

Ici, on sort donc directement le contenu de tables_principales['spip_tableau']['field']

Enfin, on peut utiliser le critère {fonction=toto()} à la place du critère var afin d’itérer sur un tableau retourné par une fonction.

Attention aux infos que vous allez chercher avec ça : si vous listez le tableau auteur_session par exemple, vous allez mettre en cache vos propres infos de session que les internautes suivants récupéreront dans le cache. => mettre le délai à zéro dans ce cas.

comment ça marche : le code est très proche de celui de la boucle for, à part qu’il y a un critère valeur qui permet de récupérer le champ valeur de la boucle au dessus. Là, il y aurait un point à améliorer car on ne peut pas trouver une valeur de 2 boucles au dessus (je pensais pouvoir utiliser la fonction index_pile, mais ça retourne une valeur de l’étage courant).

La boucle if

Je sens que celle-là, elle va être sujet à polémique :-)

Il s’agit d’évaluer une condition et de rentrer dans la boucle ou pas selon sa valeur. Le tag <//B_..> peut alors servir de partie ’else’.

Voici un exemple basique (et donc complètement pipo :-) :

<BOUCLE_a(ARTICLES){tout}>
 #ID_ARTICLE
 <BOUCLE_if(IF){condition #ID_ARTICLE > 1}>OUI</BOUCLE_if>NON<//B_if>
</BOUCLE_a>

(attention, la syntaxe a changé depuis la version précédente)

La condition peut contenir à peu près tout ce qu’on mettrait dans un if php.

comment ça marche : comme les autres, sauf que cette fois, on ne génère pas une boucle, mais un if. La condition est récupérée comme les autres paramètres, sauf qu’en plus on fait un eval dessus pour pouvoir évaluer les opérations au lieu d’avoir simplement une chaîne contenant le texte de la condition.

La condition est parfois évaluée de façon exotique, il faut donc généralement faire des tests et ne pas hésiter à regarder le code généré pour comprendre pourquoi un if ne se comporte pas comme prévu.
Par exemple, je suis tombé sur des cas ou un #MACHIN==truc ne marchait pas alors que [(#MACHIN|={truc})] marchait nickel.

utilisation détournée : il arrive qu’on veuille mettre du html autour d’une boucle si elle contient des choses, d’où l’utilisation des blocs <B_..> et </B_..>.

Mais si on veut afficher des trucs que si il y a quelquechose parmi plusieurs boucles ? Et bien il suffit de l’encadrer d’un bloc IF avec une condition "1" :

<B_bloc>
 avant
<BOUCLE_bloc(IF){cond=1}><BOUCLE_a(..)>
 ...
</BOUCLE_a><BOUCLE_b(...)>
 ...
</BOUCLE_b></BOUCLE_bloc>
 apres
</B_bloc>

Si aucune des deux boucles a et b ne sortent de résultats, les textes avant et apres ne sont pas affichés. Attention, il faut coller les tags pour ne pas sortir d’espaces ou de retours la ligne qui empècheraient d’avoir un résultat vraiment vide.

et après

J’ai dans un coin un début de boucle fichiers qui permet d’itérer sur des fichiers du disque (ce qui permettrait de faire un explorateur de fichiers coté serveur) et à l’occas, on pourrait également coder une variante du foreach sur du xml. on bouclerait alors sur des paires tag / contenu. Il faudrait en plus coder un moyen d’écrire #TAG{attribut}

Si ça vous botte, je vous le laisse comme exercice ;-)

Documents joints

  • Document (TGZ - 3 ko)

    les 4 codes des boucles décrites et un exemple pour chacune


Répondre à cet article

  • Merci pour ces codes, notamment la boucle_tableau que j’utilise sur un site que je ne peux pas passer en 1.9 !

    Je note cependant un comportement assez étrange : la boucle tableau me retourne un élément dont la clé, au lieu de porter une valeur numérique, porte la valeur ’spip_recursions’... du coup j’ai un enregistrement de trop dans mes résultats, que j’ai beaucoup de mal à filtrer (car je fais une autre boucle derrière...)

    une boucle de ce type :

                    <B_pays_tableau>
                    <h2>Liste des pays cochés</h2>
                    <ul>
                    <BOUCLE_pays_tableau(TABLEAU){var=pays}>
                            <li>Clé : #CLE | Valeur : #VALEUR | Nom : <BOUCLE_nom_pays(MOTS){id_mot=#VALEUR}>#TITRE</BOUCLE_nom_pays></li>
                    </BOUCLE_pays_tableau>
                    </ul>
                    </B_pays_tableau>
                    <h2>Aucun pays n'est sélectionné...</h2>
                    <//B_pays_tableau>

    Me donne le résultat suivant :

    Liste des pays cochés

    • Clé : 0 | Valeur : 137 | Nom : Austria
    • Clé : 1 | Valeur : 7 | Nom : Cyprus
    • Clé : 2 | Valeur : 89 | Nom : Estonia
    • Clé : 3 | Valeur : 10 | Nom : France
    • Clé : spip_recursions | Valeur : 1 | Nom : Titre n’ayant rien à voir

    Comment se débarrasser de cet enregistrement plutôt gênant ?

    Merci !

    Répondre à ce message

    • spip_recursions 14 avril 2008 21:26, par piif

      Ça ressemble à un conflit entre variables de même nom, mais je n’ai jamais constaté ça.
      Par contre, j’ai déjà eu des comportements louches quand il y a des boucles "normales" imbriquées dans des boucles tableau. ça ressemble bien à ton problème.
      Sans être devant ce code exact, avec la même version de spip, c’est pas évident d’en dire plus, désolé

      Répondre à ce message

      • spip_recursions 17 avril 2008 17:00, par Cyril MARION

        J’ai enlevé la 2eme boucle imbriquée dans la boucle tableau :

        <B_pays_tableau>
               <h2>Liste des pays cochés</h2>
               <ul>
               <BOUCLE_pays_tableau(TABLEAU){var=pays}>
                   <li>Clé : #CLE | Valeur : #VALEUR</li>
               </BOUCLE_pays_tableau>
               </ul>
               </B_pays_tableau>
               <h2>Aucun pays n'est sélectionné...</h2>
               <//B_pays_tableau>

        Mais j’obtiens toujours cette ligne de résultats en trop :

        Liste des pays cochés

        • Clé : 0 | Valeur : 7
        • Clé : 1 | Valeur : 89
        • Clé : 2 | Valeur : 14
        • Clé : 3 | Valeur : 92
        • Clé : spip_recursions | Valeur : 1

        Quand aux noms de variables, je n’ai aucune idée de là où je peux agir.

        Répondre à ce message

    Retour au début des forums

  • est ce que la boucle if accepte plusieurs conditions ? Je cherche à n’afficher un bloc dans un boucle article que si une des balises que je veux afficher dans le bloc est présente et sinon ne pas afficher le bloc

    Répondre à ce message

    • Oui, on peut mettre plusieurs conditions, mais la syntaxe est assez ésotérique, du fait qu’on mixe du spip et du php. Voici un exemple :

      <BOUCLE_si(IF){condition (#ENV{machin}|=={#MACHIN}|?{1,0}) && #ENV{truc}|=={#TRUC}|?{1,0})}>...

      on a donc 2 conditions avec un && pour faire un ET entre les 2. chacune d’elle effectue un |== pour comparer à quelquechose, mais comme ce filtre ne retourne rien si la condition est fausse, il faut enchainer sur un |? pour avoir 0 ou 1 explicitement.

      La syntaxe #ENV{machin}==#MACHIN && #ENV{truc}==#TRUC nécessite de mettre des apostrophes ou des guillemets, mais dans ce cas, le parseur spip se paume. Je n’ai donc pas trouvé de solution plus simple.

      Répondre à ce message

    Retour au début des forums

  • les exemples

    8 août 2006 08:49, par piif

    Je viens de rapatrier le tar.gz et il contient bien les fichiers. Problème de transfert lors de votre download ?

    Sinon, ce code existe désormais sous forme de plugins pour 1.9, ici

    Répondre à ce message

    Retour au début des forums

  • Les exemples

    7 août 2006 22:27, par Didier

    Bonjour,

    Je ne trouve rien dans le fichier d’archive, il semble vide. Y a-t-il une autre façon d’obtenir les exemples ?

    Merci d’avances

    Répondre à ce message

    Retour au début des forums



Suivre la vie du site RSS 2.0 | Plan du site | Espace privé | Charte et vie SPIP-Contrib | SPIP | L'autre.net