How to Create a Tag Cloud Using a Custom Data Source
I promised to follow up a previous article with a few examples like this one. If you haven't already, and you are new to custom data sources, you should read that article before reading this one. This is provided as an example only; it is not necessarily the best way to implement a tag cloud.
If your site content includes a section with a Tag List field (zero, one, or more words or phrases separated by commas) you might want to display the tags along with their frequency of use. One way to do that is to just list the tags along with a count of all the section entries that contain the tags. Another is to list the tags without the count but set the font size in proportion to the frequency of use. I use the latter method, called a tag cloud, on my archives page. Either way you will need to extract from your database a list of all the tags used and a count of the entries that contain the tags.
I found that a custom data source is the easiest way to do this. On my site a tag is a less formal entity than a category. I have a categories section that contains a set of pre-defined category names. Other section entries then link to one of the pre-defined categories. Tags, however, are not pre-defined and section entries are not limited to just one tag. That is, the only place the tag words or phrases exist is in the Tag List field of some section's entries.
To produce the needed list of all the tags used along with each tag's usage count, create a data source shell and replace its grab function with this:
function grab(&$param_pool) {
$result = new XMLElement("tagcloud");
$tags = $this->_Parent->Database->fetch(
"SELECT DISTINCT(handle), COUNT(handle) AS count, value
FROM sym_entries_data_28 GROUP BY handle ORDER BY handle ASC");
foreach($tags as $tag){
if ($tag["value"] != '') {
$tag_node = new XMLElement("tag", $tag["value"]);
$tag_node->setAttributeArray(array("handle" => $tag["handle"],
"count" => $tag["count"]));
$result->appendChild($tag_node);
}
}
return $result;
}
In my implementation the contents of the Tag List field are stored in the sym_entries_data_28 table. You will need to change that to refer to the correct table on your site. And remember to set the allowEditorToParse function to return false.
The XML output will look something like this:
<tagcloud>
<tag handle="tree" count="5">Tree</tag>
<tag handle="vine" count="3">Vine</tag>
<tag handle="blue-grass" count="2">Blue Grass</tag>
<tag handle="pine-needles" count="2">Pine Needles</tag>
<tag handle="fence" count="1">Fence</tag>
<tag handle="gate" count="1">Gate</tag>
...
</tagcloud>
To display the tag cloud use the following XSLT:
<xsl:template match="data">
<xsl:apply-templates select="tagcloud/tag"/>
</xsl:template>
<xsl:template match="tag">
<xsl:variable name="tag_count" select="@count"/>
<xsl:choose>
<xsl:when test="$tag_count > 14">
<a style="font-size: 2.4em;" href="{$root}/archives/tag/{@handle}/">
<xsl:value-of select="."/>
</a>
<xsl:text> / </xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="font_size" select="@count * 0.1 + 0.9"/>
<a style="font-size: {$font_size}em;" href="{$root}/archives/tag/{@handle}/">
<xsl:value-of select="."/>
</a>
<xsl:text> / </xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
(Credit Nick Dunn for suggesting this approach. Look here for an earlier discussion of this approach on the Symphony Forum. It also mentions another way to accomplish this.)