Hoe implementeer ik een simpel tagging systeem?
Tagging is simpelweg het toekennen van labels aan een item. Eigenlijk is dit een verkapte vorm van aan een item categorieën toekennen. Het grote verschil tussen categorieën en tags is dat tags veel vrijer zijn. Categorieën worden meestal vooraf gedefinieerd en later wordt hier een artikel aan gehangen. Bij tagging is het precies andersom. Je creëert een artikel, en hangt daar tags aan.
Bekende voorbeelden van tagging zijn te zien op Del.icio.us en Flickr. Het mooie hieraan is dat iedereen zijn/haar bookmarks / foto's een x aantal tags meegeeft, waardoor anderen deze foto's en bookmarks op basis van die tag kan vinden.
Naar mijn mening wordt tagging tegenwoordig te pas en te onpas gebruikt. Op websites als flickr en del.icio.us zijn er meerdere gebruikers die allen meerdere items taggen. Hierdoor krijg je al snel een community gevoel. Voor een één gebruiker website (portfolio, weblog, whatever) kan een tagcloud overbodig zijn, zeker als je toch maar over een x aantal onderwerpen schrijft. In dat geval is het gebruik van categorieën aan te raden. Waarom zou je als één gebruiker website toch kiezen voor een tagging systeem? Dit zou je kunnen doen doordat je visueel kan laten zien waar je interesse/kennis ligt, door middel van de text grootte.
Hoe maak je zo'n tag systeem? Onder de lees meer link een tutorial hoe je een dergelijk systeem bouwt.
A place to keep your tags
Zo, dan nu aan de slag! Ik ga er van uit dat je zelf wel een artikel in je database kan opslaan. In deze howto zal steeds gesproken worden over een artikel waar tags aan gehangen worden.
Laten we beginnen met het maken van de database. Ik gebruik onderstaande MySQL tabellen.
-
CREATE TABLE `tag` (
-
`ID` int(11) NOT NULL AUTO_INCREMENT,
-
`tag` varchar(255) NOT NULL DEFAULT '',
-
`tagged` int(11) NOT NULL DEFAULT '0',
-
PRIMARY KEY (`ID`)
-
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
-
-
-
CREATE TABLE `artikelTagRelatie` (
-
`ID` int(11) NOT NULL AUTO_INCREMENT,
-
`pID` int(11) NOT NULL DEFAULT '0',
-
`tID` int(11) NOT NULL DEFAULT '0',
-
PRIMARY KEY (`ID`)
-
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
Inserten die tags!
De tabel `tag` heeft de kolom 'tag' voor de naam van de tag, en de 'kolom' tagged is het aantal keer dat een tag gebruikt is. Dit hebben we nodig voor de front-end code zodat we een tagcloud te maken. Dat komt zo. Laten we eerst eens tags gaan toevoegen. Hiervoor heb je een extra text input veld nodig in je artikel formulier. Ik noem deze "tags" en vul deze door de tags met comma te scheiden. Vaak zie je tag velden op spatie gescheiden maar hier ben ik zelf geen fan van omdat dat het aanmaken van een tag met 2 woorden een stuk lastiger maakt.
Na het posten van dit complete formulier insert je eerst het artikel, en haalt hierna het ID van deze database entry op. Hierna explode je de tag string op de comma. Dit geeft een array met alle tags terug, waarna we ze kunnen gaan inserten of updaten.
Let op: Zoals altijd op Scriptorama bevatten de code voorbeelden in dit artikel geen juiste fout afhandeling. Dit doen we zodat de code die we proberen te tonen zo kort en zo duidelijk mogelijk is.
-
/* Het ID van je artikel*/
-
-
/* We gaan er van uit dat $_POST['tags'] bestaat. */
-
-
foreach($tagArray as $rawTag)
-
{
-
-
-
/* Controleer of de tag al bestaat en verhoog het nummer in de kolom 'tagged' met 1. Bestaat
-
* de tag nog niet, maak deze dan aan. */
-
-
$tagId = 0;
-
if ($numRows != 0)
-
{
-
$tagId = $recordTag['ID'];
-
}
-
else
-
{
-
$sqlInsertTag = "INSERT INTO `tag` (`tag`, `tagged`) VALUES ('$tag', '1')";
-
-
}
-
-
$sqlInsertRel = "INSERT INTO `artikelTagRelatie` (`pID`, `tID`) VALUES ('$pID', '$tagId')";
-
}
De user interface
Leuk en aardig, zo zul je wellicht denken, nu heb ik wel tags in mijn database maar hoe zit het dan met de front-end? Een veel gebruikte techniek om tags te tonen is het gebruik van een tag cloud. Dit is een overzicht van de gebruikte tags waarbij de veel gebruikte tags groter getoond worden dan minder gebruikte tags. Zo ziet de gebruik in een oogopslag welke tags belangrijk zijn.
Om dit te maken hebben we het volgende nodig:
- alle tags en het aantal keer dat de tag voorkomt. Dit kunnen we uit de 'tag' tabel vissen.
- een systeem wat de font grootte gerelateerd maakt aan hoe vaak een tag gebruikt is.
-
$resultTag = mysql_query("SELECT MAX(`tagged`) AS max_tagged, MIN(`tagged`) AS min_tagged FROM `tag`");
-
-
$maxTagged = $recordTag['max_tagged'];
-
$minTagged = $recordTag['min_tagged'];
-
-
-
$resultHtml = '';
-
-
{
-
/* Bepaal de font-size als een waarde tussen de 100 en de 200: */
-
-
$fontPercentage = 100 / ( $maxTagged - $minTagged );
-
$cssPercentage = ($record['tagged'] - $minTagged) * $fontPercentage + 100;
-
-
$resultHtml .= '<span style="font-size:'.$cssPercentage.'"><a href="?tag='.htmlspecialchars($record['tag']).'">' . htmlspecialchars($record['tag']). '</a></span>';
-
-
}
-
-
Dit geeft je een hele rij met tags terug, met een percentage als font-size. Dit kan er (na wat CSS werk) ongeveer zo uit gaan zien:

De link gaat in mijn geval naar de pagina waar je je al op bevindt, met als GET value de tag. Op die pagina zal je dan moeten kijken of er een tag meegestuurd wordt en of deze tag wel mag (dus geen potentieel gevaar met zich meebrengt, xss / sql injection). Is dit het geval, dan kan je met die tag een MySQL query uitvoeren die je de artikels geeft die met de mee gestuurde tag getagged zijn. Hiervoor kan je onderstaand script gebruiken. De SQL query zal je nog wel even moeten aanpassen
-
$sql = "SELECT -watjemaarwil- FROM `-artikeltabel-`, `tag`, `artikelTagRelatie` WHERE `tag`.`tag` = '$tag' AND `artikelTagRelatie`.`tID` = `tag`.`ID` AND `artikelTagRelatie`.`pID` = `-artikeltabel-`.`ID`";
-
-
// voer query uit en toon wat je wilt laten zien.
-
}
-
}
Over de auteur
Ik ben Joost van Velzen, een oud collega en nog steeds bijna-buurman van Mathieu. Ik werk als webdeveloper bij NR6 en hoop Scriptorama af en toe van wat nuttigs te voorzien :). Mijn eigen site, waar dit tagging systeem ook in gebruik is, vind je op joostvanvelzen.net.
Volg Scriptorama via RSS!
Reageer ook!
De manier om te kijken of een tag al bestaat verijst veel database interactie.
Ik zelf gebruik een andere manier, maar deze moet ik nog uitwerken en heb dus geen voorbeeld.
Mijn manier is op basis van een array welke bijhoud welke al bestaan en wat het verschil tussen de nieuwe ingevoerde waarde.
Het geheel kan werken met een loop maar verijst maar een query voor het ophalen.
Door Sebastiaan Stok
op 05.02.07 @ 6:32 pm | Permalink
Volgens mij is de kolom tagged (`tagged` int(11) NOT NULL DEFAULT '0') in de tabel tag niet noodzakelijk.
Met behulp van de tabel artikelTagRelatie zou je het aantal maal dat een tag voorkomt kunnen tellen.
Heb je een speciale reden hiervoor? Ik ben wel benieuwd.
Door Jip
op 05.03.07 @ 11:07 am | Permalink
Ik ben eigenlijk ook wel benieuwd. ;) Een reden die ik kan bedenken is dat je de 'zware' sql-queries in je back-end houdt en lichtere queries in je front-end. Een soort caching? Dit zou ik echter anders doen in dat geval. :)
Door Alexander
op 05.03.07 @ 11:18 am | Permalink
Wat Alexander zegt. Liever zware queries bij het invoeren aan de achterkant dan aan de voorkant bij het ophalen. Dit tagging systeem is natuurlijk niet het systeem, maar een systeem. Er zullen ongetwijfeld 1000en manieren zijn om zoiets voor elkaar te krijgen. Kijk voor de grap eens door de broncode van `Ultimate Tag Warrior` http://www.neato.co.nz/ultimate-tag-warrior/ Die pakken het toch net even anders aan.
Door Joost van Velzen
op 05.03.07 @ 12:02 pm | Permalink
Als je van het veld 'tag' nu eens in de database afdwingt dat het een UNIQUE is ?
Dat is eigenlijk de bedoeling als ik zo naar de code kijk. Bovendien zal het het je een select query en een -trage- num_rows actie schelen.
Door Boaz
op 05.04.07 @ 6:18 pm | Permalink
En dan moet je nogsteeds een query doen om het ID van de tag te bepalen.
Waarom is mysql_num_rows() traag volgens jou? Ik heb hier echt nog nooit last van gehad.
Door Joost van Velzen
op 05.05.07 @ 7:09 am | Permalink
In de code voor de user interface is een haakje vergeten:
while ($record = mysql_fetch_assoc($cloudResult)
wordt
while ($record = mysql_fetch_assoc($cloudResult))
Door Wim
op 06.21.08 @ 2:36 pm | Permalink
Bedankt Wim, dat haakje miste ik ook inderdaad. Verder super artikel
Door webdesign
op 02.25.09 @ 9:04 pm | Permalink
Line and paragraph breaks automatic, e-mail address never displayed, HTML allowed:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>