[spip-dev] Jointures de tables automatiques

Bonjour,

Je continue mes expérimentations avec les tables externes de SPIP
(épatante fonctionnalité, entre nous soit dit), et j'ai trouvé une
incohérence :

J'ai une table externe que SPIP nomme «externe», et voilà une boucle qui
marche :

<BOUCLE_projets_joints(externe:project scientist){last_name="Singes"}>
   #LAST_NAME : #NAME
</BOUCLE_projets_joints>

project a deux champs : id_project et name
scientist a trois champs : id_scientist, id_project et last_name

Comme vous le voyez, "name" est un champ de projet et "last_name" un
champ de "scientist", il y a donc jointure (sur id_project) et voilà le
SQL généré :

SELECT L1.last_name, project.name
FROM project AS `project`
INNER JOIN scientist AS L1 ON ( L1.id_project = project.id_project )
WHERE (L1.last_name = 'Singes')
GROUP BY project.id_project

C'est parfait !

À présent, si je remplace {last_name="Singes"} par {par last_name}, ça ne
marche plus car la jointure essaie de se faire sur "id_scientist"...

Voilà le SQL généré :

SELECT L1.last_name, project.name
FROM project AS `project`
INNER JOIN scientist AS L1 ON ( L1.id_scientist = project.id_scientist )
ORDER BY last_name

Ce qui plante, c'est que project n'a pas de champ id_scientist.
J'aimerais que la jointure se fasse encore sur id_project : comment le
forcer ?

Version de SPIP : Stable branche 2.0 [14057]

Amicalement,

Trois Singes a écrit :

À présent, si je remplace {last_name="Singes"} par {par last_name}, ça
ne marche plus car la jointure essaie de se faire sur "id_scientist"...

Je vais un peu plus loin dans ma légère analyse du code :

La première jointure est calculée par calculer_chaine_jointures (dans
ecrire/public/jointures.php) : la fonction est subtile et fonctionne bien.

En revanche, la seconde jointure est calculée par index_exception (dans
ecrire/public/references.php), et là c'est la cata, vu que la fonction
choisit systématiquement la PRIMARY KEY de la seconde table comme
variable de jointure (sans vérifier s'il n'y a pas une autre clé qui
marche). L'algorithme est beaucoup moins subtil ici.

Je me demande s'il y a un moyen d'utiliser calculer_chaine_jointures dans
index_exception, au lieu du mini-algorithme qui y est actuellement.

À suivre...

Amicalement,

Trois Singes a écrit :

Je me demande s'il y a un moyen d'utiliser calculer_chaine_jointures dans index_exception, au lieu du mini-algorithme qui y est actuellement.

D'ailleurs, avant que je cherche plus avant, est-ce que vous savez pourquoi ça passe par index_exception plutôt que par calculer_chaine_jointures dans certains cas ?

Pour te dépanner, tu peux imbriquer 2 boucles et spécifier ainsi la jointure
:

Cf exemple dans http://www.spip-contrib.net/Utiliser-les-tables

<BOUCLE_bouclemachine(MACHINES)>
<B_bouclemesure>
    Pour la machine #NUMMACHINE<br/>
<BOUCLE_bouclemesure(MESURES){idmachine=#NUMMACHINE}{}>
    #NANNEE/#NSEMAINE, mesure = #VALMESURE unités.<br/>
</BOUCLE_bouclemesure>
    <hr/>
</B_bouclemesure>
</BOUCLE_bouclemachine>

@+

Ch.
-----Message d'origine-----

Christophe Boutin a écrit :

Pour te dépanner, tu peux imbriquer 2 boucles et spécifier ainsi la jointure
:

Cf exemple dans Utiliser les tables complémentaires (dites aussi tables extra) - SPIP-Contrib

Merci Christophe pour ton message. En fait, mes squelettes sont déjà sous la forme de boucles imbriquées (puisque ça marche), et je voulais les transformer pour utiliser les jointures (parce que c'est plus clair dans le squelette).

Amicalement,

Ok, et n'hésite pas si tu veux enrichir l'article de ton expérience.
Ce sera profitable pour tous !

Amicalement,

Christophe

-----Message d'origine-----

As tu compris ou résolu ceci qui ressemble à un bug ?
JL

Trois Singes a écrit :

JLuc a écrit :

As tu compris ou résolu ceci qui ressemble à un bug ?

Non, je n'y ai pas passé trop de temps, je reprendrai le sujet la semaine prochaine et vous tiendrai au courant.

Amicalement,

Trois Singes a écrit :

Bonjour,

<BOUCLE_projets_joints(externe:project scientist){last_name="Singes"}>
   #LAST_NAME : #NAME
</BOUCLE_projets_joints>

À présent, si je remplace {last_name="Singes"} par {par last_name}, ça ne marche plus car la jointure essaie de se faire sur "id_scientist"...

As tu essayé {par scientist.last_name} ?

Matthieu Marcillaud a écrit :

Trois Singes a écrit :

Bonjour,

<BOUCLE_projets_joints(externe:project scientist){last_name="Singes"}>
   #LAST_NAME : #NAME
</BOUCLE_projets_joints>

À présent, si je remplace {last_name="Singes"} par {par last_name}, ça ne marche plus car la jointure essaie de se faire sur "id_scientist"...

As tu essayé {par scientist.last_name} ?

Oui, et ça donne le même mauvais résultat.

Trois Singes a écrit :

La première jointure est calculée par calculer_chaine_jointures (dans ecrire/public/jointures.php) : la fonction est subtile et fonctionne bien.

En revanche, la seconde jointure est calculée par index_exception (dans ecrire/public/references.php), et là c'est la cata, vu que la fonction choisit systématiquement la PRIMARY KEY de la seconde table comme variable de jointure (sans vérifier s'il n'y a pas une autre clé qui marche). L'algorithme est beaucoup moins subtil ici.

Etat de mes pérégrinations après une heure de boulot nocturne :

La première jointure est calculée par la suite d'appels suivants (xdebug est votre ami) :

assembler > public_parametrer_dist > public_composer_dist > public_compiler_dist > *calculer_liste* > compile_cas > calculer_champs > calculer_balise > index_pile > index_tables_en_pile > index_exception > fabrique_jointures

On repère bien l'appel coupable à index_pile.

La deuxième jointure, celle qui marche, est calculée par cette suite d'appels :

assembler > public_parametrer_dist > public_composer_dist > public_compiler_dist > *calculer_criteres* > calculer_critere_DEFAULT > calculer_critere_infixe > calculer_critere_externe_init > calculer_jointure > fabrique_jointures

Ca diverge dans public_compiler_dist : en effet, comme la première boucle n'a pas de "critère" du genre {last_name="Singes"} mais seulement un tri, le compilateur n'appelle pas "calculer_criteres" mais plutôt "calculer_liste".

D'ailleurs, ça me permet de trouver une solution de contournement :

<BOUCLE_projets_joints(externe:project scientist){par last_name}{last_name!=1}>
    #LAST_NAME : #NAME
</BOUCLE_projets_joints>

Là ça fonctionne ! En effet, il y a un critère (toujours vrai, c'est ça l'astuce, puisqu'il n'y a personne qui s'appelle 1), donc le compilateur appelle calculer_criteres qui est un pro pour calculer les bonnes jointures :slight_smile:

Donc, ce qu'il reste à faire (pas cette nuit), c'est patcher index_exception (ou une autre fonction de la pile d'appels) pour que le calcul de la jointure soit faite à la manière de calculer_jointure (voire même PAR calculer_jointure). C'est du boulot :-/

Stay tuned !