Les articles connexes
Il est très intéressant de pouvoir présenter à nos visiteurs les articles qui traitent du même sujet que celui qu’ils sont en train de lire. Cela peut se faire simplement en listant les articles présents dans la même rubrique. On peut aussi le faire plus finement, en présentant aux visiteurs la liste des articles qui partagent les mêmes mots-clés que l’article qu’ils sont en train de consulter. Ce sont les articles connexes (pas forcément dans la même rubrique, mais reliés par leurs mots-clés à l’article courant).
Avec Spip, il est simple de faire la liste des articles connexes. Pour chaque mot-clé rattaché à l’article consulté, on affiche la liste des articles qui ont ce mot-clé. Cela donne la noisette suivante à placer dans vos squelettes d’articles :
Cette noisette qui contient une boucle dans une autre qui affiche le titre et un lien de chaque article qui a un mot-clé commun avec l’article de contexte [1]. Ainsi, on a la liste des articles connexes. On peut affiner en informant le visiteur des mots-clés communs grâce à cette autre noisette à placer dans la boucle _Articles_Connexes :
Dans cette noisette, #_Contexte_Article:ID_ARTICLE signifie #ID_ARTICLE de la boucle _Contexte_Article, à ne pas confondre avec l’#ID_ARTICLE de la boucle dans laquelle on place la noisette (_Articles_Connexes). Pour en savoir plus sur la syntaxe des balises du type : #_Nom_Boucle:#NOMBALISE allez consulter la documentation de Spip sur les Balises non ambiguës.
Même chose pour #_Mots_Communs:TOTAL_BOUCLE.
Donc, vous devez adapter cette petite noisette à votre cas, si vous êtes dans une boucle globale qui se nomme _Contexte_Article, pas de problème ; sinon, n’oubliez pas de changer son nom.
Tout ça, c’est très bien... Mais à condition d’avoir peu d’articles, et de mots-clés. Car, si comme moi, vous avez beaucoup d’articles et qu’ils ont chacun beaucoup de mots-clés ; la liste des articles connexes devient rapidement interminable (songez qu’elle contient tous les articles qui ont au moins un mot-clé commun avec l’article visité !). Pour éviter d’avoir 200 articles listés à chaque fois on peut se contenter de mettre dans la boucle _Articles_Connexes un critère {0,20} qui liste les 20 premiers articles par mots-clés. Bien sûr si vous avez beaucoup de mots-clés, il faudra lister moins de 20 articles par mots-clés, et peut-être pas tous les mots-clés non plus... Beaucoup de contraintes finalement et une solution peu satisfaisante qui est de prendre les x premiers articles des n premiers mots-clés, sans tenir compte de la pertinence de ce choix (dans les x premiers articles, il peut y en avoir qui n’ont qu’un seul mot-clé commun avec celui visité, alors qu’on voudrai plutôt afficher ceux qui en ont le plus).
Comment trier les —trop nombreux— articles connexes par pertinence et n’afficher que les plus intéressants pour le visiteur ?
Principe de la contrib’
Les articles connexes les plus pertinents sont ceux qui ont le plus de mots-clés communs avec l’article consulté. C’est ceux-là que l’on veut voir s’afficher en premier.
La contrib’ adapte donc la noisette présentée plus haut, qui est classique, pour pouvoir compter le nombre de mots-clés communs associés à chaque article connexe.
Spip, permet depuis la version 1.9.2 de faire des tableaux et de les manipuler avec les balises #SET et #GET introduites avec Spip 1.9.1 (voir la documentation de Spip). Mais, pour bien comprendre comment les utiliser, il faudra au préalable comprendre comment faire sans les utiliser. Pour cela il faut utiliser du PHP.
Noisette et noix de coco
Il faut toujours être dans un contexte d’article (donc que id_article soit défini).
On met en place le compteur pour chaque article qui a des mots-clés communs. Avec ce compteur on compte le nombre de mots-clés en commun. C’est dans un tableau en PHP. Voilà la noisette :
Le tableau $pertinence[ ] contient donc la liste des articles ayant des mots-clés communs avec notre article de contexte. Il contient aussi, pour chaque article de la liste, une valeur de pertinence qui correspond au nombre de mot-clés communs avec l’article de contexte.
On veut afficher les articles qui ont plus de 4 mots-clés communs (variable $maxcommun à changer selon vos goûts) avec notre article de contexte. On ne veut pas afficher plus de 20 articles (variable $maxarticles à changer selon vos goûts) au total.
Cette petite noisette grosse noix de coco est entrelacée dans du php : le tableau est trié de manière à ce que les articles les plus pertinents soient traités en premier. Puis, pour chaque élément du tableau, on va faire une boucle qui affiche un lien vers l’article, le nombre de mots-clés communs et la liste de ces mots-clés.
Et cela, seulement si la pertinence est suffisante (condition sur $maxcommun) et qu’il y a moins de 20 articles connexes affichés (condition sur $maxarticles)
Cette noix de coco est complexe parce qu’elle est imbriquée dans du PHP...
À propos de l’intégration du PHP
J’ai découvert pas mal de choses sur l’intégration du PHP dans Spip. Ce qu’il faut principalement retenir ce sont les étapes du travail de Spip, telles qu’elles sont expliquées par ARNO* [2] ici : « Le PHP est interprété après les boucles ».
Donc, quand on intègre du PHP dans Spip, on doit systématiquement le séparer du reste par des balises <?php et ?> qui indiquent où commence et où finit le PHP.
Si vous souhaitez, comme dans cette contrib’, utiliser des valeurs calculées par Spip, ou issues de la base de donnée dans votre script PHP, récupérez-les en suivant cette méthode : Comment récupérer une « variable spip » en une variable PHP ?
Autre découverte : on peut couper le script PHP par ces balises partout, même au milieu d’un test de condition, si on oublie pas de le finir un peu plus loin (toujours encadré par les balises <?php et ?>). C’est ce que je fais dans la seconde noisettex, en commençant un test sur le tableau (« while (list($ks,$vs) = each($pertinence)) { » dont la fin se situe plus loin dans un autre « bloc » de PHP).
Dernière découverte et grosse difficulté (exposée par ARNO*) : on ne peut pas récupérer de variable PHP dans Spip car les BOUCLE/#BALISE sont transformées en PHP avant que le contenu du fichier ne soit lu par le serveur web ! Heureusement, les contributeurs ont trouvé deux solutions.
l’une simple que j’ai adopté, qui est expliquée ici : utiliser une variable PHP pour sélectionner une rubrique (par exemple). Ce n’est pas très élégant, car cela m’oblige à faire une boucle dans laquelle l’ensemble des articles est sélectionné, ce qui coûte un peu au serveur web/MySQL.
L’autre est élégante et puissante, elle utilise l’environnement de la page : en allemand : Tricks mit HTTP-Vars (moi je ne parle pas l’allemand ! Mais, la voici en plus ou moins français : variable php dans un critère de boucle ?). Elle est trop complexe pour moi.
Sans PHP et en beaucoup plus rapide !...
(mise à jour du 12/05/2008)
La méthode décrite plus haut fonctionne bien, mais présente deux désavantages :
- elle met du PHP dans le squelette, ce qui en plus de ne pas être élégant pose des problèmes d’efficacité avec le cache de Spip.
- elle nécessite de faire plusieurs passages par une boucle (
BOUCLE_Articles_Connexes) qui liste tous les articles. Sur de grosses bases de données, cela peut prendre des proportions considérables !
Jusqu’à SPIP 1.9.2 c’était la seule possible. En utilisant les #ARRAY de Spip, il est possible de faire sans PHP et donc d’éviter ces deux problèmes.
Le principe sera exactement le même. On va faire une première boucle imbriquée pour fabriquer un tableau qui liste les articles ayant des mots-clés communs avec une pondération (nombre de mots-clés communs). Puis nous allons afficher les articles ayant la plus forte pondération.
On ne peut éviter totalement de faire appel au PHP pour arriver à nos fins. Mais grâce aux filtres de Spip, nous pouvons éviter d’en mettre. Les filtres sont appliqués sur les balises Spip. Il peuvent être définis dans le fichier mes_fonctions.php à placer dans le dossier squelettes. Les filtres peuvent aussi être des fonctions PHP. Nous allons utiliser les deux.
Si vous avez un fichier mes_fonctions.php, vous devrez rajouter le code ci-dessous dedans. Sinon, vous devrez en créer un que vous placerez dans votre dossier squelettes :
- <?php
- //Incrémente la valeur correspondant à une clé dans un tableau.
- //Est utilisé pour incrémenter la pondération des articles.
- function ponderation ($tableau,$cle) {
- $tableau[$cle]++;
- return $tableau;
- }
- ?>
- <?php
- //Trie un tableau en fonction de ses valeurs
- //Est nécessaire, car [(#GET{un_tableau}|asort)] ne semble pas fonctionner
- function trie_tableau ($tableau) {
- return $tableau;
- }
- ?>
Nous y définissons deux filtres sur les tableaux. L’un incrémente la valeur correspondant à une clé d’un tableau, l’autre trie un tableau.
Reprenons notre squelette... Commençons par pondérer les articles en fonction de leurs mots-clés communs avec l’article de contexte :
À la place du PHP, nous utilisons l’un de nos filtres pour augmenter la pondération de nos articles dans un tableau Spip (notez que le tableau est en fait initialisé par le filtre). La balise #SET sert à affecter une valeur à une variable Spip (ici tableau_pertience), #GET permet de récupérer le contenu de la variable.
L’objectif est de pouvoir utiliser un tableau Spip pour notre boucle BOUCLE_Articles_Connexes :
Pour rendre notre tableau utilisable dans une boucle, il va falloir lui faire subir des transformations :
classer les articles par pondération (ils sont dans le désordre dans le tableau)
Transformer le tableau pour qu’il soit une liste d’id_article seulement, car les id_article sont pour l’instant les clés du tableau, les valeurs sont les pondérations elles-mêmes. Alors que la boucle aura besoin d’un tableau où les id_article seront des valeurs [3].
Voici la manœuvre :
Le premier filtre est l’un du fichier mes_fonctions.php, qui trie le tableau. Ce tableau subit un second filtre, qui est une fonction PHP qui renvoie un tableau avec seulement les clés d’un autre tableau : array_keys.
Nous aurons aussi besoin de connaître la pertinence maximum des articles. C’est la première valeur de notre tableau tableau_pertience une fois trié :
C’est reset qui retourne cette valeur dans notre variable pertinence_max.
Passons aux choses sérieuses :
Cette boucle écrit les titres des 20 articles ({0,20}) ayant le plus de mots-clés communs avec l’article passé en contexte.
Mais pourquoi avoir récupéré pertinence_max ? Pour afficher la pertinence en pourcentage : c’est pondération de l’article*100/pondération_max. Soit en Spip :
table_valeur donne la valeur stockée dans un tableau pour une clé donnée (ici #ID_ARTICLE). Pour le reste, ce sont des filtres mathématiques de Spip.
Voilà, là noix qui découle de tout ceci est :
N’oubliez surout pas de créer ou de completer cotre fichier mes_fonctions.php sinon, ça ne va pas fonctionner !
Voilà, j’espère que cela vous sera utile. Je trouve en tout cas très agréable de pouvoir proposer à mes visiteurs une liste restreinte d’articles connexes qui sont toujours pertinents, car ils partagent un nombre de mots-clés important.
Et vive la navigation transversale !

