Sorting tags by those that have most items associated with the tag

So I am wanting to try and sort my tags by those that have the highest number of items associated to that tag.

For example

tag1 = 4 itmes
tag2 = 2 items
tag3 = 5 items

Expected output

tag3 = 5 items
tag1 = 4 items
tag2 = 2 items

Reading this https://getkirby.com/docs/cookbook/content/sorting#sorting-with-page-models I can see that I can do this by a page model, but I can’t figure out the logic I would need.

templates/albums.php

<ul class="o-filter">
            <li class="o-filter__item"><a href="<?= $page->url() ?>" class="o-filter__link">All</a></li>
            <?php foreach ($tags as $tag): ?>
                <li class="o-filter__item">
                    <a href="<?= url($tagurl, ['params' => ['tag' => urlencode($tag)]]) ?>" class="o-filter__link">
                        <?= $tag ?>
                    </a>
                </li>
            <?php endforeach ?>
        </ul>

controllers/albums.php

<?php

return function ($page) {

    // Albums
    $albums = $page->children()->listed();

    // Tags
    $tags = $albums->pluck('tags', ',', true);
    $tagurl = $page->url();

    if ($tag = urldecode(param('tag'))) {
        $albums = $albums->filterBy('tags', $tag, ',');
    }

    // Pagination
    $album = $albums->paginate(2);
    $pagination = $album->pagination();

    return [
        'album' => $album,
        'albums' => $albums,
        'pagination' => $pagination,
        'tags' => $tags,
        'tagurl' => $tagurl
    ];

};

Can someone please point me in the right direction.

Many thanks.

If you create a new Collection from your tags, you can then map a virtual sort field, where the number you assign to this field is then number of articles with this tag. You can then sort by this field.

$tags = $albums->pluck('tags', ',', true);
$tags = new Collection($tags);
$sortedTags = $tags->map(function($item) {
  $item->sortNo = 'here the number of children';
  return $item;
})->sortBy('sortNo');

Not sure this is what I’m looking for. I had trouble trying to explain this in my own head let alone write it xD

An article has a tags field, where anyone can assign any number of tags to the article.

So for example I could end up with something like this.

article1 = time, color, beauty (3 tags)
article2 = hope, time (2 tags)
article3 = love (1 tag)
article4 = time, hope, beauty, color, wood, metal (6 tags)
article5 = metal (1 tag)

Within my current tag collection this will obviously output all tags

time, color, beauty, hope, love, wood, metal

I now want to order the tag output on the front-end so that the tags with the highest number of articles associated with it are first (rank higher)

So the expected output would be this

time (3 articles), color (2 articles), beauty (2 articles), hope (2 articles), metal (2 articles), love (1 articles), wood (1 articles)

On each article page or in a tag cloud? Because in your controller above, you pluck all tags or all articles…

It’s to live on the article list page not the article detail page. So I guess it’s a tag cloud, yes, that’s why I pluck all tags for all articles.

Ok, so theoretically the code I posted above should do exactly the right think, only it seems that this doesn’t work with Kirby 3 collections anymore. I’ll come up with a solution.

Ok, here we go:

$albums = $page->children()->listed();
$tags = $albums->pluck('tags', ',', true);

// flip the array to prevent duplicate keys
$tags = array_flip($tags);

// assign the number of albums with that tag to the value
array_walk($tags, function(&$value, $key) use($albums) {
  return $value = $albums->filterBy('tags', $key, ',')->count();

});
// reverse sort the array
arsort($tags);
dump($tags);

You can now loop through the array, and if you want, assign the number of occurrences

<ul>
    <?php foreach ($tags as $tagName => $frequency): ?>
        <li><?= $tagName . ' (' . $frequency . ')' ?></li>
    <?php endforeach ?>
</ul>
1 Like