Comment créer un nuage de tags en PHP/MySQL ?
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 ».
Il faudrait que tu viennes une fois aux MySQL User Group
Pourquoi pas (même si je suis loin d’être un pro) ? Tu peux en dire un peu plus ?
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
Euh… je crois que je reviendrais quand il sera moins tard et que j’aurais les idées plus claires.
Great Site, Yeah. Hope you can improve it again!
Marrant, mon PHP 5.2 connait pas la constante MAX_INT.
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…
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…
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.
Merci Gimi
coool
j’ai bon espoir de réussir à faire des nuages de mots dans le système que je développe !
merci,
jérô
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…
mercii tu va beaucoup m’aider
The site looks great ! Thanks for all your help ( past, present and future !)
hi
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à…
Plougy, il s’agit d’un alias de count(*) :
SELECT tag, count(*) as number FROM tags GROUP BY tag ORDER BY tag
Merci!
Sinon comment gérer des tags pour différentes catégories (Vidéo, Blogs, News) ?
Problème résolu Vinch !
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
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 !!
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.
Cool ! Je proposerai certainement une version modifiée se basant sur tes requêtes SQL dans quelques temps
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
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
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
Rien à voir mais je voulais te dire que j’aime vraiment bien ton « blog ».
Merci Viktor
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
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
Tu rajoute à la fin de ta rquête :
//—CODE —//
LIMIT (0,29)
//— FIN CODE —//
Je suis un elève qui cherche un emploi à l’etranger pour etre plus serieux dans mon travail.
Très utile.
Merci beaucoup pour ce partage.
les nuages son cool
tu pue le pette poilu
les nuages son fait de base de gase carbuonique (pete de cheval)
allo bye bye
Thks !!!!
Adapté et mis en place en 10 minutes chrono !
Super
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!!!!!!!!
Super cette solution pour générer des nuages de tags, bien simpa
et prochainement réutilisé jpense
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
Merci beaucoup pour la formule magique
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