Les tags sont devenus un élément incontournable pour toute application Web 2.0 qui se respecte. Ils permettent une organisation qui va plus loin que la catégorisation classique (catégorie > sous-catégorie > etc.). Cette classification est appelée Folksonomie.
Un nuage de tags, quant à lui, est une façon de représenter un groupe de tags, en jouant visuellement sur la taille et la couleur de la police. Les exemples les plus marquants de nuages de tags sont ceux de Flickr, de Technorati ou encore de del.icio.us.
Dans ce billet, je vais tenter de vous montrer comment on peut réaliser son propre nuage de tags en PHP/MySQL.
Tout d’abord, vous avez besoin d’une table dans votre base de données, qui se présente à peu près de cette façon :
+---------+ | tags | +---------+ | id | | tag | | item_id | +---------+
* id est l’identifiant unique du tag
* tag est le tag à proprement parler
* item_id est l’élément auquel vous attachez le tag, par exemple un billet de votre blog, un lien, une vidéo, etc. Il s’agit d’une clé étrangère vers l’identifiant unique de la table concernée.
Une fois remplie, votre table doit ressembler à quelque chose de ce genre :
+-----+------+---------+ | id | tag | item_id | +-----+------+---------+ | 1 | fun | 1 | | 2 | html | 1 | | 3 | css | 1 | | 4 | html | 2 | | 5 | php | 2 | | 6 | css | 3 | | 7 | php | 3 | | 8 | php | 4 | +-----+------+---------+
Voyons maintenant le code PHP qui va nous permettre de recolter les données utiles à la création de notre nuage. Je vous épargne les lignes concernant la connexion à la base de données.
<?php
define("MIN_SIZE", 9);
define("MAX_SIZE", 36);
$result = mysql_query("SELECT tag, count(*) as number FROM tags GROUP BY tag ORDER BY tag") or die(mysql_error());
$min = MAX_INT;
$max = -MAX_INT;
while ($tag = mysql_fetch_assoc($result)) {
if ($tag['number'] < $min) $min = $tag['number'];
if ($tag['number'] > $max) $max = $tag['number'];
$tags[] = $tag;
}
$min_size = MIN_SIZE;
$max_size = MAX_SIZE;
foreach ($tags as $tag) {
$tag['size'] = intval($min_size + (($tag['number'] - $min) * (($max_size - $min_size) / ($max - $min))));
$tags_extended[] = $tag;
}
?>
Les deux premières lignes servent à définir les tailles minimum et maximum. La taille minimum sera assignée aux élements ayant la plus petite fréquence (ici fun avec une fréquence 1) et la taille maximum aux éléments ayant la plus grande fréquence (ici php avec une fréquence 3). Ce sont des tailles en pixels, vous pouvez bien sûr changer ces valeurs ou adapter l’algorithme pour travailler en pourcentages.
Ensuite, on réalise une requête SQL qui extrait chaque tag distinct accompagné de sa fréquence.
La boucle while sert à récupérer la fréquence minimum et la fréquence maximum (dans notre exemple, respectivement 1 et 3). Avant la boucle, on associe à $min et $max des valeurs de départ (très grande pour le minimum et inversément) qui seront remplacées dès le premier passage dans la boucle.
Dans le foreach, on calcule la taille qui sera associée à chaque tag grâce à une formule magique. Cette formule ajoute à la taille minimum la fréquence moins la fréquence minimum fois un certain ratio. Ce ratio correspond à l’écart de tailles entre deux fréquences voisines. Dans notre exemple, la différence de taille entre deux fréquences voisines est de 12,5 (36-9/3-1).
Cela donne donc le résultat suivant : la taille des éléments de fréquence 1 est 9, la taille des éléments de fréquence 2 est 21 (9+12,5 arrondi à l’unité inférieure via intval), la taille des éléments de fréquence 3 est 36 (9+(2*12,5)). On est parfaitement retombé sur nos pattes.
Au niveau de l’affichage, il vous suffit de passer le tableau $tags_extended à votre vue et de boucler dessus pour afficher les tags, en utilisant un style en ligne pour les tailles :
<?php foreach ($tags_extended as $tag) : ?> <a href="/tags/<?php echo $tag['tag']; ?>" style="font-size:<?php echo $tag['size']; ?>px" title="<?php echo $tag['tag']; ?>"><?php echo $tag['tag']; ?></a> <?php endforeach; ?>
Si vous utilisez le moteur de templates Smarty (ce que je vous conseille), cela donne :
{foreach from=$tags_extended item=tag}
<a href="/tags/{$tag.tag}" style="font-size:{$tag.size}px" title="{$tag.tag}">{$tag.tag}</a>
{/foreach}
Voici le résultat auquel on arrive. Vous pouvez également voir le contenu des tableaux $tags et $tags_extended sur cette page.
Pour les techniciens, sachez que la complexité de cet algorithme est en O(2*n) où n est le nombre de tags distincts dans la base de données.
Dans un prochain billet, j’essaierai de rendre ce nuage de tags accessible ! (je n’ai pas encore de solutions miracles mais j’y travaille)
sympa. Pour améliorer les performances, il est recommandé d’ajouter un index sur le champ “tag”.
LikeLike
Il faudrait que tu viennes une fois aux MySQL User Group
LikeLike
Pourquoi pas (même si je suis loin d’être un pro) ? Tu peux en dire un peu plus ?
LikeLike
Il suffit d’être “utilisateur” et comprendre l’anglais 🙂
les 2 premiers ont eu lieu en flandre. Le 3ème probablement à Leuven.
pour la wallonnie, il faut trouver un lieu… une date…
http://moosh.et.son.brol.be/blog/index.php/tag/mysql
LikeLike
Euh… je crois que je reviendrais quand il sera moins tard et que j’aurais les idées plus claires.
LikeLike
Great Site, Yeah. Hope you can improve it again!
LikeLike
Marrant, mon PHP 5.2 connait pas la constante MAX_INT.
LikeLike
J’ai entendu dire qu’il y avait plein de problèmes de retrocompatibilité dans PHP 5.2
Ils ont certainement placé MAX_INT à la trappe…
LikeLike
Est-ce normal que je n’ai pas tout compris ? 😦
Je n’ai pas très bien compris à quoi correspondait GROUP By (dans la requête sql) ainsi que la totalité de foreach… 😥
Help me…
LikeLike
A partir du moment où tu introduis une fonction de calcul dans le select (ici count()), et que ce count n’est pas la seule colonne (ici tag), il te faut spécifier la clause Group by.
LikeLike
Merci Gimi 😉
LikeLike
coool
j’ai bon espoir de réussir à faire des nuages de mots dans le système que je développe !
merci,
jérô
LikeLike
très bon travail, par contre deux trois petits améliorations/corrections 🙂
– je vois plutot un $min = $max = 1; (aucune valeur ne peut être nulle, en plus ca règle le problème de compatibilité)
– heu l’affectation des constantes à des variables, pourquoi ?! ( $minsize et $maxsize )
– plutot qu’allouer un nouveau tableau (extended), mieux vaut modifier l’actuel (charge mémoire!):
foreach (arraykeys($tags) as $id) {
$tag =& $tags[$id];
$tag[‘size’] = intval($minsize + (($tag[‘number’] – $min) * (($maxsize – $min_size) / ($max – $min))));
}
– bug possible: $min = $max (d’où une division par 0): tu peux faire un $max++ ou bien tester ce cas avant la boucle…
LikeLike
mercii tu va beaucoup m’aider 🙂
LikeLike
The site looks great ! Thanks for all your help ( past, present and future !)
LikeLike
hi
LikeLike
Dans la requête effectué, je ne comprend pas la valeur numbers vu qu’il n’y pas de ligne dans la table de ce nom là…
LikeLike
Plougy, il s’agit d’un alias de count(*) :
SELECT tag, count(*) as number FROM tags GROUP BY tag ORDER BY tag
LikeLike
Merci! 😉
Sinon comment gérer des tags pour différentes catégories (Vidéo, Blogs, News) ?
LikeLike
Problème résolu Vinch ! 😉
LikeLike
Bonjour,
je souhaiterais installer votre système a mon site internet ttefois je souhaiterais savoir si je ne pouvais avoir les fichiers en zip car je ne suis pas programmeur.
Merci a vous
LikeLike
Bonjour,
J’ai réussi à faire ce nuage en SQL seulement !
SELECT VT.tags, (
ROUND((36 / tmp2.min ) * ( tmp2.min / tmp2.max ) * COUNT( VT.tags )
)) AS valeur
FROM video_tags AS VT, (
SELECT MAX( tmp.nbre ) AS max, MIN( tmp.nbre ) AS min, (
MIN( tmp.nbre ) / MAX( tmp.nbre )
) AS ratio
FROM (
SELECT tags, COUNT( tags ) AS nbre
FROM
video_tagsGROUP BY tags
)tmp
)tmp2
GROUP BY tags
ORDER BY valeur DESC
Requête générée en 0.0326 sec !!
LikeLike
Enfin c’est pas encore tout à fait au point, car ça ne gère pas encore la police MINIMALE, mais bon si on trouve une valeur inférieur à X on peut redefinir la police directement en PHP.
LikeLike
Cool ! Je proposerai certainement une version modifiée se basant sur tes requêtes SQL dans quelques temps 😉
LikeLike
Bon voici la requête terminée, je pense.
Avec donc, la limitation pour la taille de police minimale.
Et la selection aléatoire des tags à afficher !
Dans ce cas, police maxi de 36, minimum de 6 et tirage aléatoire de 10 tags
SELECT VT.tags,
CASE WHEN ROUND( ( 36 / tmp2.min ) * ( tmp2.min / tmp2.max ) * COUNT( VT.tags ) ) >6
THEN ROUND( ( 36 / tmp2.min ) * ( tmp2.min / tmp2.max ) * COUNT( VT.tags ) )
ELSE 6
END AS valeur
FROM video_tags AS VT, (
SELECT MAX( tmp.nbre ) AS max, MIN( tmp.nbre ) AS min, (
MIN( tmp.nbre ) / MAX( tmp.nbre )
) AS ratio
FROM (
SELECT tags, COUNT( tags ) AS nbre
FROM
video_tagsGROUP BY tags
)tmp
)tmp2
GROUP BY tags
ORDER BY RAND()
LIMIT 0 , 10
LikeLike
salut
je voulais savoir pour la db en ce qui concerne l’item_id il serait possible de générer un classement pertinent du style
SELECT * FROM tags WHERE MATCH(tags) -> AGAINST (‘+pertinence, -pertinence’ IN BOOLEAN MODE)
?
Pour être plus précis, lorsqu’un article s’affiche, je voudrais que le nuage de tags classe par pertinence tous les tags qui seraient connexes à la thématique de l’article lu.
Merci car j’avoue je sèche 😉
LikeLike
Ma question va peut-être parraitre simpliste à beaucoup d’entre vous mais s’il était possible que quelqu’un m’explique plus en détail la solution en sql, ça serai sympa parce que j’avou que j’ai pas tout compris… :s
LikeLike
Rien à voir mais je voulais te dire que j’aime vraiment bien ton “blog”.
LikeLike
Merci Viktor 😉
LikeLike
Bonjour,
j’aimerai integrer le systeme de tags dans le moteur de recherche de mon site , ou chaque mot clé devient un tags mais dans ce cas la je risque pas d’exploser ma base de donnée vu que j’ai beaucoup de visites donc beaucoup de recherche … une petite idée sur le probleme ?
merci
LikeLike
bien, bien, moi aussi j’ai mis 65535 et -65535 pour Maxint et -Maxint !!! mais ca marche bien…
Par contre, c’est une liste infinie qu’on obtient ? je crois.
Comment fait on pour limiter la talle des tags, par exemple, si je veut les 20 tags les plus usités ???
Merci
LikeLike
Tu rajoute à la fin de ta rquête :
//—CODE —//
LIMIT (0,29)
//— FIN CODE —//
LikeLike
Je suis un elève qui cherche un emploi à l’etranger pour etre plus serieux dans mon travail.
LikeLike
Très utile.
Merci beaucoup pour ce partage.
LikeLike
les nuages son cool
LikeLike
tu pue le pette poilu
LikeLike
les nuages son fait de base de gase carbuonique (pete de cheval)
LikeLike
allo bye bye
LikeLike
Thks !!!!
Adapté et mis en place en 10 minutes chrono !
Super
LikeLike
slt je ve un code source en html/php: cmt céer des tags sur mon site svp c trés urgent et merci d’avance!!!!!!!!
LikeLike
Super cette solution pour générer des nuages de tags, bien simpa 🙂 et prochainement réutilisé jpense 🙂
LikeLike
Merci !! adapté et mis en place en un rien de temps !
Question subsidiaire : et si on voulait limiter le nombre de tag aux 10 les plus courants ??
Super et encore merci
LikeLike
Merci beaucoup pour la formule magique 😀
LikeLike
J’ai souci qui me bloque depuis longtemps sur les tags. Comment faire pour les inserer dans la BDD sachant que je veux taguer un articles avec plusieurs tags tout ca en une seule requete.
Sur les sites qui proposent de d’ajouter des tags, il est souvent recommender d’insere une virgule apres chaque tag. C’est la ou mon cerveau ne capte pas comment faire. Merci Vinch
LikeLike
Bon travaille merci pour le billet. Mais j ai une question: Comment remplir la table. J ai besoin du formulaire et la requete qui insert les tag..?
LikeLike
Moi problème ce que je suis entraîne de créer un site et le client veut me demande de gréer un tagcloud qui
permet d’afficher les nuages des mots clés a chaque visite dans le site,le mots clés en gras et à different couleurs………..!
merçi à tous
LikeLike