I kind of want a tag archive page where it doesn't just list all the tags and links to entries based on that tag, but where the sizes of the actual tag link vary based on frequency of use.
Any idea how?
Brandon commented on getting tag usage count (and how it can be expensive) in this Google+ post and again in this SE question where I was trying to think through a plugin.
So to completely steal from that, you could loop through your tags and get a count for each...
{% set tags = craft.tags.find() %}
{% for tag in tags %}
{% set count = craft.entries.section('blog').relatedTo(tag).total() %}
<a href="/entries/{{ tag.slug }}" data-count="{{ count }}">{{ tag.name }}</a>
{% endfor %}
Then manipulate the listing with JavaScript or find the max count and loop again to apply inline CSS percentages or font-size
s that are scaled accordingly. As Brandon noted in that post, be sure to wrap everything in {% cache %}
tags since this will be an intense template to render.
Update:
Since this answer, Phil Birnie released a plugin for this he's linked to below, and I came up with my own that we trashed in a Straight Up Craft Hangout thanks to Marion's stellar idea of using a Preparse field to tally counts, which I've added here as an alternative answer.
Marion Newlevant had a clever, efficient idea in this Straight Up Craft Hangout if you don't mind having a slight lag (like a day) for your tag counts to be up to date. That idea: store the counts you need in a Preparse field on each tag, and set up a cron job to re-save all tags periodically.
Preparse is a free plugin from André Elvan that renders Twig code to a field when an entry is saved. Install it, add a Preparse field to your Tag elements (like tagCount
) for example, and make the contents of said field {{ craft.entries.section('blog').relatedTo(tag).total }}
, where blog
is the section for which we're building the tag cloud. Be sure to set Column Type → Number, since we're storing a count and we'll sort on it later in our template.
When a Tag is saved, Craft will count the entries related to it and store the total in this new tagCount
field. Now we just need to make sure tags are saved every now and then!
Here we'll make a publicly-accessible PHP script that'll get an instance of Craft (which is easy) and tell it to resave our tags. In this case we'll save all tags, but you can limit the scope in the task settings if you need to.
In your /public folder, create a file called resave-tags.php
and drop this in it:
<?php
namespace Craft;
$app = require("../craft/app/bootstrap.php");
$app->tasks->createTask('ResaveElements', 'Resaving tags!', array(
'elementType' => 'Tag'
));
Note that you don't have to make this public, you can run it from a terminal, shell script, or any old way you'd normally interact with PHP.
Now you should be able to hit yourdomain.craft.dev/resave-tags.php and go confirm that Craft has updated your tagCount
values. (You did already set up entries and tags, right?) Now we just need our reliable friend cron to trigger this for us periodically.
If you've never used cron, it's a very simple, powerful tool that lets you run commands at intervals, whether it's every second or every second Tuesday or every decade. The process for editing cron tasks depends on your hosting environment, but the vast majority give you some way to edit these tasks.
For this example, we'll hit our URL at midnight every day and ignore the output (because we're brave):
0 0 * * * /usr/bin/wget --spider \"http://foo.dev/resave-elements.php\" >/dev/null 2>&1
Here we'll get our tags and order by tagCount
starting with the greatest number. Using just a dash of math, we'll loop through entries and get a number we can use to size text—the biggest tags will be 4em
and the smallest will be 1em
, per minScale
and maxScale
:
{% set tags = craft.tags.order('tagCount desc').limit(999) %}
{% if tags | length %}
{% set minScale = 1 %}
{% set maxScale = 4 %}
{% set biggestTagCount = (tags|first).tagCount %}
{% set smallestTagCount = (tags|last).tagCount %}
<ul class="cloud">
{% for tag in tags %}
{% set fontSize = ((tag.tagCount - smallestTagCount) / biggestTagCount) * (maxScale - minScale) + minScale %}
<li>
<span style="font-size: {{ fontSize }}em;">
{{ tag.title }}
</span>
</li>
{% endfor %}
</ul>
{% endif %}
And that's one way to make a visually-weighted tag cloud.