I'm attempting to write a plugin to extend tags by adding a count
property that'd be useful identifying popular tags, making a tag cloud, and anything else where a count would be useful.
The Goal
What I'd like is to neatly extend TagModel
so that interacting with these new count tallies would be straightforward and as similar as possible to working with tags. For example, pretend superTags
is my count-appended set and I'm getting the top ten tags for a site across sections:
{% for tag in superTags.limit(10).order('count desc') %}
{{ tag.name }} has been used {{ tag.count }} times.
{% endfor %}
...or getting the top 10 tags by a blog author:
{% for tag in superTags.channel('blog').relatedTo(author).limit(10).order('count desc') %}
{{ tag.name }} was born in {{ tag.dateCreated | date('Y') }}.
{% endfor %}
The Problem
I humbly admit that I'm struggling to see the big picture. For example, this works fine (my plugin's called Taggregate):
{% set tags = craft.tags.find() %}
{% set superTags = craft.taggregate.appendCounts(tags) %}
{% set superTags = craft.taggregate.sortByCount(superTags) %}
{% for tag in superTags %}
{{ tag.name }} has been used {{ tag.count }} times.<br>
{% endfor %}
I've got a TaggregateModel
that extends TagModel
only to merge in a count
Number to defineAttributes()
. My Service takes the tags array, then spits out a new one with TaggregateModel
objects that include the count
property. (Each count is just a tally from craft_relations
with no regard for other parameters like section or author—later I intend to use an aggregate table as Brad suggested, even though that's irrelevant here.)
TaggregateService
...
public function appendCounts($tags)
{
$superTags = array();
new TaggregateModel($tags);
for ($i=0; $i < count($tags); $i++)
{
$superTags[$i] = new TaggregateModel($tags[$i]);
$superTags[$i]->count = $this->getTagCount($superTags[$i]->id);
}
return $superTags;
}
public function getTagCount($tagId)
{
$q = craft()->db->createCommand('SELECT count(*) as count FROM craft_relations WHERE targetId = '.$tagId)->queryAll();
// TODO: get stored aggregate if it exists, otherwise count and store
return $q[0]['count'];
}
public function sortByCount($tags)
{
usort($tags, function($a, $b)
{
return $a->count < $b->count;
}
);
return $tags;
}
...
While this works, it's clearly bad design:
- The Twig interface is specific to the plugin's weird version of non-native methods.
- This
superTag
array isn't smart enough to do any of the cool things thatTagModel
can.
The Question
Is there a more sensible way to build this so I can hit my ideal usage outlined in "The Goal" above? I'm not looking for somebody to write this for me, just some big-picture guidance since I suspect I'm making this harder than it should be.
...or is my seemingly-simple objective actually complicated?