Filter (and count) collections using categories and tags

Hey!
First time writing and I’m trying to filter a collection using both categories and tags.

Right now I’m filtering using categories and my controller looks like this:

<?php 

	return function ($page, $site) {

		$filterBy = get('filter');
		$unfiltered = collection('photography')->sortBy(function ($page){
  			return $page->date()->toDate();}, 'desc');
		$projects = $unfiltered
			->when($filterBy, function($filterBy){
				return $this->filterBy('category', $filterBy);
			})
			->paginate(16);

		$pagination = $projects->pagination();
		$filters = $unfiltered->pluck('category', null, true);
		$tags = collection('photography')->pluck('tags', ',', true); sort($tags);

	return [
		'filterBy' => $filterBy,
		'unfiltered' => $unfiltered,
		'projects' => $projects,
		'pagination' => $pagination,
		'filters' => $filters,
		'tags' => $tags,
	];
};

The filter navigation looks like this

<nav class="filter">
  <a href="<?= $page->url() ?>">All</a>
  <?php foreach ($filters as $filter): ?>
  <a href="<?= $page->url() ?>?filter=<?= $filter ?>"><?= $filter ?></a>
<?php endforeach ?>
</nav>

I’m also displaying a list of all the tags using this snippet

    <ul class="tags">
      <?php foreach($tags as $tag): ?>
      <li>
        <a href="<?= url('photography', ['params' => ['tag' => $tag]]) ?>">
          <?= html($tag) ?>
        </a>
      </li>
      <?php endforeach ?>
    </ul>

Here’s a link to the actual page
https://christopherlandin.com/photography

If you click the “List all Genera” button, all tags are shown and the URL’s seems to be correct. But I’m having problems getting the actual filtering to work.

What would be the proper way to implement this in my controller?

Bonus question, I’d also love to show how many projects are tagged with a specific tag when displaying the list.

Something like

      <?php foreach($tags as $tag): ?>
      <li>
        <a href="<?= url('photography', ['params' => ['tag' => $tag]]) ?>">
          <?= html($tag) ?> (<?php **COUNT-PAGES-WITH-THIS-TAG** ?>)
        </a>
      </li>
      <?php endforeach ?>

I’ve tried some snippets found on the forum, but haven’t figured out how to use count() in regards to tags.

Thanks in advance, any help would be appriciated!

All the best,
C

You can chain multiple when() methods here: $pages->when() | Kirby CMS

Thank you! :slightly_smiling_face:

I got it to work using

		$filterBy = get('filter');
		$filterTags = param('tag');
		$unfiltered = collection('photography')->sortBy(function ($page){
  			return $page->date()->toDate();}, 'desc');
		$projects = $unfiltered
			->when($filterBy, function($filterBy){
				return $this->filterBy('category', $filterBy);
			})
			->when($filterTags, function($filterTags){
				return $this->filterBy('tags', $filterTags);
			})	
			->paginate(16);

As for my second question, would it be possible to count the number of pages within a specific tag? I’ve been playing around with echo count and array unique

      <?php foreach($tags as $tag): ?>
      <li>
        <a href="<?= url('photography', ['params' => ['tag' => $tag]]) ?>">
          <?= html($tag) ?> (<?php echo count(array_unique($page->tags()->split())); ?>)
        </a>
      </li>
      <?php endforeach ?>

But I’m not trying to list and count all tags for a specific page, I’m trying to list all tags and display the number of projects within each tag…

Any help would be greatly appriciated :pray:

You have to filter the collection for each tag and then count the elements in the filtered collection

Yes, that is the part I’m struggling with.
I’m now trying to add this to my controller

$result = $unfiltered->filterBy('tags', $filterTags)->count();

And then access it in the foreach-loop when displaying the tags

    <ul class="tags">
      <?php foreach($tags as $tag): ?>
      <li>
        <a href="<?= url('photography', ['params' => ['tag' => $tag]]) ?>">
          <?= html($tag) ?> (<?= html($result) ?>)
        </a>
      </li>
      <?php endforeach ?>

Which is outputting “0”, so I guess there’s something I’m missing in the first part.

You can’t filter your collection in the controller, you have to pass the full collection to the template, then filter inside your loop:


<ul class="tags">
      <?php foreach($tags as $tag): ?>
      <li>
        <a href="<?= url('photography', ['params' => ['tag' => $tag]]) ?>">
          <?= html($tag) ?> (<?= $unfiltered->filterBy('tags', $tag, ',')->count(); ?>)
        </a>
      </li>
      <?php endforeach ?>
</ul>
1 Like

Wow, thank you so much! :heart:
That makes sense and works like a charm :slightly_smiling_face:

My last task is to output the total number of species listed on the website.
For each project I’m writing the individual name in a subheadline text-field in the panel.

Is it possible to list all subheadline() and merge the duplicates (fields displaying the same name) and then count them?

Something like

<?= $unfiltered->pluck('subheadline', ',', true) *merge* ->count(); ?>

(I’m sorry if I’m asking too many questions in one thread)