[spip-dev] Comment passer des données dynamiques à un critère {par ...} ?

Bonjour,

Pour répondre à un besoin d'ordonnancement de présentation d'une liste d'articles, j'ai essayé une boucle du genre :
<BOUCLE_articles(ARTICLES MOTS){id_article IN ...}}{par FIELD(id_article,...)}>
  ...
</BOUCLE_articles>

J'ai constaté que "FIELD(id_article,...)" était bien pris en considération (reproduit tel que dans la clause ORDER BY de la requête), et ça répondait donc à mon besoin.

Du moins en codant "en dur" la liste "..." dans "FIELD(id_article,...)".
La réalité du besoin, c'est que cette liste est calculée en dynamique au préalable.
Et là, problème...
J'ai essayé plusieurs choses :

{par FIELD(id_article,#EVAL{ma_fonction_qui_renvoie_la_liste()})}
--> arrête l'interprétation (erreur "Critère inconnu : par FIELD(id_article,")

{par FIELD(#EVAL{ma_fonction_qui_renvoie_la_liste_avec_id_article_avant()})}
--> c'est la requête qui n'est pas alimentée : "ORDER BY FIELD", sans plus

{par #EVAL{ma_fonction_qui_renvoie_carrément_FIELD(id_article_et_la_liste)()}}
--> la requête est alimentée, mais en supprimant parenthèses et virgules : "ORDER BY FIELDid_article123456..."

J'ai aussi essayé d'utiliser plutôt #SET{var,#EVAL{...}} en amont, suivi de {par #GET{var}}, dans les mêmes combinaisons --> mêmes résultats

(nota : #EVAL{...} tout seul affiche bien le contenu attendu, issu de la fonction)

Enfin j'ai même essayé #EVAL{ma_fonction()}, avec ma_fonction qui génère carrément toute la boucle --> c'est encore pire, puisque la boucle est simplement considérée comme texte.
(j'en déduis que les #EVAL sont juste interprétés "au passage", alors qu'on aurait pu espérer qu'ils le soient dans un premier balayage du script, suivi d'un second pour interprétation des boucles...)

Au passage, j'ai du mal à comprendre pourquoi l'interprétation est aussi disparate, d'un genre de codage à l'autre.
Mais surtout, quel moyen y a-t-il de contourner cette difficulté ?

Merci d'avance

Je me réponds à moi-même... du moins pour une partie de la question.
En effet je viens de découvrir qu'un critère du genre {id_article IN ...} définit par défaut le tri dans l'ordre donné par liste IN...
Je n'ai donc pas besoin, dans ce cas précis, de faire appel à {par FIELD(id_article,...)}.

Cependant, la question reste entière en ce qui concerne le moyen GENERAL d'introduire des éléments dynamiquement variable dans les critères d'une boucle.
De même que celle du pourquoi des insertions par par #EVAL, plus ou moins fidèles selon la construction.

Il me semble qu'il faut absolument passer par un INCLURE. Par exemple :

             #SET{tri, date}
             #SET{senstri,1}
             #SET{nb,5}
             [(#SET{suite,<:tous_les_articles:>})]
             [(#REM) Compagnies croisees ]
             [(#ID_SECTEUR|=={4}|oui)
                #SET{tri,titre}
                #SET{senstri,0}
                #SET{nb,10}
                 [(#SET{suite,<:toutes_les_compagnies:>})]
             ]
<INCLURE{fond=inc_menu_mot_cle3-articles}
                         {env}{id_rubrique=#ID_RUBRIQUE}{tri=#GET{tri}}{senstri=#GET{senstri}}{nb=#GET{nb}}{suite=#GET{suite}}>

Et dans inc_menu_mot_cle3-articles.html :
<BOUCLE_menu_articlesss(ARTICLES){id_rubrique}{lang ?} {par #ENV{tri,num titre}}{inverse #ENV{senstri,0}} {0, #ENV{nb}}>

J'ai l'impression qu'il y a erreur de ciblage du message : j'avoue que je ne vois pas le rapport entre cette réponse et ma question ?
Et pardon à Yffic si j'ai mal compris.

"Yffic" a écrit dans le message de groupe de discussion : 4CE5A3AD.8030606@lefourneau.com...

Je me réponds à moi-même... du moins pour une partie de la question.
En effet je viens de découvrir qu'un critère du genre {id_article IN ...} définit par défaut le tri dans l'ordre donné par liste IN...
Je n'ai donc pas besoin, dans ce cas précis, de faire appel à {par FIELD(id_article,...)}.

Cependant, la question reste entière en ce qui concerne le moyen GENERAL d'introduire des éléments dynamiquement variable dans les critères d'une boucle.
De même que celle du pourquoi des insertions par par #EVAL, plus ou moins fidèles selon la construction.

Il me semble qu'il faut absolument passer par un INCLURE. Par exemple :

             #SET{tri, date}
             #SET{senstri,1}
             #SET{nb,5}
             [(#SET{suite,<:tous_les_articles:>})]
             [(#REM) Compagnies croisees ]
             [(#ID_SECTEUR|=={4}|oui)
                #SET{tri,titre}
                #SET{senstri,0}
                #SET{nb,10}
                 [(#SET{suite,<:toutes_les_compagnies:>})]
             ]
<INCLURE{fond=inc_menu_mot_cle3-articles}

{env}{id_rubrique=#ID_RUBRIQUE}{tri=#GET{tri}}{senstri=#GET{senstri}}{nb=#GET{nb}}{suite=#GET{suite}}>

Et dans inc_menu_mot_cle3-articles.html :
<BOUCLE_menu_articlesss(ARTICLES){id_rubrique}{lang ?} {par #ENV{tri,num
titre}}{inverse #ENV{senstri,0}} {0, #ENV{nb}}>

J'ai l'impression qu'il y a erreur de ciblage du message : j'avoue que je ne vois pas le rapport entre cette réponse et ma question ?
Et pardon à Yffic si j'ai mal compris.

Pas de probleme... Je voulais repondre a la question "la question reste entière en ce qui concerne le moyen GENERAL d'introduire des éléments dynamiquement variable dans les critères d'une boucle"...
Mais si tu ne comprend pas ma reponse, c'est que j'ai pas du bien comprendre ta question, ou l'inverse, enfin je ne sais plus ou alors, c'etait pas un chat

Ah oui, je comprends... désolé, c'est la différence de contexte de l'exemple qui m'avait fait louper la partie importante de la réponse : "Il me semble qu'il faut absolument passer par un INCLURE".

Et donc, si j'interprète bien, ça veut dire implicitement, en même temps, que la possibilité d'apporter une donnée dynamiquement ne peut se faire qu'avec #ENV (d'où l'obligation de passer par <INCLURE>), et que ni #EVAL ni #GET ne peuvent retourner l'info "propre" ?

Merci, je retiens l'idée, je vais faire des essais comparatifs.

"Yffic" a écrit dans le message de groupe de discussion : 4CE6AD19.8010207@lefourneau.com...

J'ai l'impression qu'il y a erreur de ciblage du message : j'avoue que je ne vois pas le rapport entre cette réponse et ma question ?
Et pardon à Yffic si j'ai mal compris.

Pas de probleme... Je voulais repondre a la question "la question reste
entière en ce qui concerne le moyen GENERAL d'introduire des éléments
dynamiquement variable dans les critères d'une boucle"...
Mais si tu ne comprend pas ma reponse, c'est que j'ai pas du bien
comprendre ta question, ou l'inverse, enfin je ne sais plus ou alors,
c'etait pas un chat

euh...

#SET{ouap, id_article}
<BOUCLE_p1(ARTICLES) {par #GET{ouap}}>
   #ID_ARTICLE<br />
</BOUCLE_p1>

fonctionne.

<BOUCLE_p2(ARTICLES) {!par #ENV{ouap}}>
   #ID_ARTICLE<br />
</BOUCLE_p2>

fonctionne avec &ouap=id_article dans l'url.

ton problème est dû à l'utilisation 'en dur' de la fonction
ORDER BY FIELD de mysql.

Salut Denis

Je viens de refaire des tests, car je ne comprenais plus pourquoi je passais par un INCLURE... Donc effectivement ce que tu propose fonctionne avec id_article. Avec date aussi. Avec titre, il n'y a pas de tri, enfin le tri se fait apparemment sur id_article. Si on rajoute le critere inverse dans la boucle, la ca provoque une erreur mysql que ce soit avec id_article, date ou titre.

La seule doc que j'ai trouvée (http://www.spip.net/fr_article900.html#inverse), indique bien de passer par #ENV (donc de faire un INCLURE) si on veut par exemple utiliser un sens de tri dynamique

ha.

en 2.1.2 dev [16589] :
   #SET{ouap, titre}
   <BOUCLE_p1(ARTICLES) {par #GET{ouap}}>
     #TITRE<br />
   </BOUCLE_p1>

me produit bien la requête (var_mode=debug) :
   SELECT rand() AS hasard,
          articles.id_article,
          articles.lang,
          articles.titre
     FROM spip_articles AS `articles`
    WHERE articles.statut = 'publie'
      AND articles.date < '2138-01-01 00:00:00'
ORDER BY articles.titre

et :
   <BOUCLE_p2(ARTICLES) {!par #GET{ouap}}>
     #TITRE<br />
   </BOUCLE_p2>

me produit bien la requête (var_mode=debug) :
   SELECT rand() AS hasard,
          articles.id_article,
          articles.lang,
          articles.titre
     FROM spip_articles AS `articles`
    WHERE articles.statut = 'publie'
      AND articles.date < '2138-01-01 00:00:00'
ORDER BY articles.titre DESC

OK, j'ai compris et c'est logique, mais je n'etais pas encore tombé la dessus : mon set etait dans la partie optionnelle avant la boucle.

Pas contre peux tu confirmer (ou infirmer) que pour le sens du tri on est oblige de passer par #ENV ?

ah pour {tri et {senstri je sais pas.
on sort du core là.

Heu non, je parle du critere inverse et de l'exemple donne la :
http://www.spip.net/fr_article900.html#inverse

hum.

#SET{ouap, titre}
#SET{pilou, 0}
<BOUCLE_p1(ARTICLES) {par #GET{ouap}} {inverse #GET{pilou}}>
   #TITRE<br />
</BOUCLE_p1>

=> ORDER BY articles.titre

#SET{pilou, 1}
<BOUCLE_p2(ARTICLES) {par #GET{ouap}} {inverse #GET{pilou}}>
   #TITRE<br />
</BOUCLE_p2>

=> ORDER BY articles.titre DESC

#SET{pilou, z}
<BOUCLE_p3(ARTICLES) {par #GET{ouap}} {inverse #GET{pilou}}>
   #TITRE<br />
</BOUCLE_p3>

=> ORDER BY articles.titre DESC

#SET{pilou, 0.005}
<BOUCLE_p4(ARTICLES) {par #GET{ouap}} {inverse #GET{pilou}}>
   #TITRE<br />
</BOUCLE_p4>

=> ORDER BY articles.titre DESC

donc *non* : pour le sens du tri on n'est pas obligé de passer par #ENV

Je reviens tardivement dans le fil, et constate qu'il a un peu dévié du problème initial.
C'est pourquoi je le reprends à partir de la réponse de denisb, ci-dessous, qui dit "ton problème est dû à l'utilisation 'en dur' de la fonction ORDER BY FIELD de mysql".

Là je ne suis pas d'accord (voir détail de mon premier post dans ce fil) :
. si je mets cet ORDER BY FIELD *en dur*, justement ça marche très bien
. par contre si je "l'apporte" dans le critère par l'intermédiaire d'un #EVAL, ou d'un #GET lui-même précédemment alimenté par un #EVAL, alors ça ne va plus : les parenthèses et les virgules sont perdus !

Voilà pourquoi je résume la question en disant que ni #EVAL ni #GET ne retournent l'info "propre".
Du moins *dans le contexte du critère* : en dehors, #EVAL et #GET retournent bien ce que j'attends.

Dit autrement, ce serait : d'après ce que je constate, #EVAL et #GET ne fonctionnent pas façon correcte lorsqu'ils sont placés à l'intérieur du critère d'une boucle.

"Je l'aurais un jour, je l'aurais"... Bon ben non, Denisb on peut pas... :wink:
Merci

Là je ne suis pas d'accord (voir détail de mon premier post dans ce fil) :
. si je mets cet ORDER BY FIELD *en dur*, justement ça marche très bien

je me suis mal exprimé donc.
ton problème est dû à l'utilisation 'en dur' de la fonction ORDER BY
FIELD de mysql à laquelle tu passes des arguments *calculés* (en
l'occurence une balise). du coup le phraseur, la fonction phraser_criteres(), se retrouve face à un objet de type 'champ' (il
cherche des 'texte') et ne sait qu'en faire...

ni #EVAL ni #GET ne
retournent l'info "propre".

dans *ce* cadre d'utilisation *précis*

d'après ce que je constate, #EVAL et #GET ne
fonctionnent pas façon correcte lorsqu'ils sont placés à l'intérieur du
critère d'une boucle.

cette affirmation (générique) est fausse.

Hmmm... j'avoue ne pas être bien convaincu...
Ce qui est retourné par mon #GET est bien du *texte*.
Du coup j'essaie de me dire que, lorsque tu dis que phraser_critères() se retrouve face à un objet de type "champ", tu fais allusion, non pas au texte retourné par #GET, mais bien à cette balise en elle-même, avant interprétation.
Mais ça ne peut pas être ça non plus, puisqu'alors aucune donnée ne pourrait être apportée par #GET.
Donc je tourne en boucle...
Et je répète, pour éviter toute ambiguïté, que le contenu retourné par le #GET est bien un texte, du genre "FIELD(1,2,3)".

"denisb" a écrit dans le message de groupe de discussion : icbder$dlu$1@dough.gmane.org...

Là je ne suis pas d'accord (voir détail de mon premier post dans ce fil) :
. si je mets cet ORDER BY FIELD *en dur*, justement ça marche très bien

je me suis mal exprimé donc.
ton problème est dû à l'utilisation 'en dur' de la fonction ORDER BY
FIELD de mysql à laquelle tu passes des arguments *calculés* (en
l'occurence une balise). du coup le phraseur, la fonction
phraser_criteres(), se retrouve face à un objet de type 'champ' (il
cherche des 'texte') et ne sait qu'en faire...

ni #EVAL ni #GET ne
retournent l'info "propre".

dans *ce* cadre d'utilisation *précis*

d'après ce que je constate, #EVAL et #GET ne
fonctionnent pas façon correcte lorsqu'ils sont placés à l'intérieur du
critère d'une boucle.

cette affirmation (générique) est fausse.

oui.
*dans ce cas précis*, la chronologie est la suivante :

#SET{ordre, 'FIELD(id_article,4,3,2,1)'}
<BOUCLE_p1(ARTICLES) {id_article IN 1,2,3,4} {par #GET{ordre}}>

phraser_args() du phraseur analyse chaque élément du critère.
elle trouve un objet de type 'texte : 'par', puis un objet
de type 'champ' : 'GET'

comme elle a détecté la présence d'un tri 'par', le critère est analysé
à travers la fonction critere_parinverse() qui reconnait qu'il s'agit
d'un tri specifié dynamiquement et envoit l'argument -toujours
l'*objet* 'champ' de nom 'ordre' et non sa *valeur*- à la fonction
calculer_critere_arg_dynamique()

celle-ci termine le travail en retournant cette fois la *valeur* du GET
*mais* nettoyée, par preg_replace("\W", '', $arg), de tout ce qui n'est
pas un caractère valide (ce nettoyage -certes peu subtil- étant là par
sécurité, pour éviter toute injection sql).

à ce stade #GET{ordre} équivaut donc à 'FIELDid_article4321'

D'accord, je comprends, techniquement.
Et je me dis que c'est dommage de procéder comme ça.

Je me serais attendu à ce qu'on puisse compter sur une possibilité *générale* de remplacement de tout #EVAL, #GET, #ENV ou #SESSION par sa valeur, avec des règles uniques quel que soit le contexte.
Et que ce remplacement, dans le cas des boucles, intervienne avant le début de l'analyse de la boucle : bel et bien, donc, du simple remplacement textuel préalable.

Outre la cohérence d'ensemble, ça mettrait beaucoup de souplesse dans... tout...
Ca dépasse largement le cas particulier qui a initié cette discussion.
Je me rappelle notamment avoir dû, il y a quelques mois, renoncer à quelque chose du genre <BOUCLE_variable(#GET{nom_table})>.
J'ai dû trouver un contournement qui m'obligeait à changer complètement de stratégie.

Et quant au nettoyage, pourquoi ne pas le rendre, comme ailleurs, simplement dépendant de la ou des * que le codeur ajoute sous sa propre responsabilité ?
D'ailleurs, j'avais essayé #GET*{FIELD(1,2,3,4)}, sans résultat.
Là encore, je trouve que c'est dommage de ne pas avoir de cohérence de comportement.

"denisb" a écrit dans le message de groupe de discussion : icd9ti$hj6$1@dough.gmane.org...

Du coup j'essaie de me dire que, lorsque tu dis que phraser_critères()
se retrouve face à un objet de type "champ", tu fais allusion, non pas
au texte retourné par #GET, mais bien à cette balise en elle-même, avant
interprétation.

oui.
*dans ce cas précis*, la chronologie est la suivante :

#SET{ordre, 'FIELD(id_article,4,3,2,1)'}
<BOUCLE_p1(ARTICLES) {id_article IN 1,2,3,4} {par #GET{ordre}}>

phraser_args() du phraseur analyse chaque élément du critère.
elle trouve un objet de type 'texte : 'par', puis un objet
de type 'champ' : 'GET'

comme elle a détecté la présence d'un tri 'par', le critère est analysé
à travers la fonction critere_parinverse() qui reconnait qu'il s'agit
d'un tri specifié dynamiquement et envoit l'argument -toujours
l'*objet* 'champ' de nom 'ordre' et non sa *valeur*- à la fonction
calculer_critere_arg_dynamique()

celle-ci termine le travail en retournant cette fois la *valeur* du GET
*mais* nettoyée, par preg_replace("\W", '', $arg), de tout ce qui n'est
pas un caractère valide (ce nettoyage -certes peu subtil- étant là par
sécurité, pour éviter toute injection sql).

à ce stade #GET{ordre} équivaut donc à 'FIELDid_article4321'

Et que ce remplacement, dans le cas des boucles, intervienne avant le
début de l'analyse de la boucle : bel et bien, donc, du simple
remplacement textuel préalable.

la valeur de la plupart des balises dépendant de la boucle (requête
sql) dont elle dépendent, il est bien nécessaire d'analyser les
boucles, (et donc en amont les critères qui leur sont associés)
préalablement aux balises.

Je me rappelle notamment avoir dû, il y a quelques mois, renoncer à
quelque chose du genre <BOUCLE_variable(#GET{nom_table})>.

la gestion dynamique des tables ne peut effectivement passer qu'à
partir d'inclure conditionnels.

la gestion conditionnelle des tables étant désormais possible avec
l'écriture : <BOUCLE_alpha(MA_TABLE ?) {crit...
voir http://programmer.spip.org/Boucle-sur-une-table-absente

Et quant au nettoyage, pourquoi ne pas le rendre, comme ailleurs,
simplement dépendant de la ou des * que le codeur ajoute sous sa propre
responsabilité ?

oui. dans le cas concerné, ce serait sans doute une possibilité (risquée dans le cas d'un critère de valeur #ENV{...).