Creating Tag Filters based on Cookbook

Hi all,
i tried implementing the cookbook example for creating a blog with the possibility to filter by tag. However i get “Undefined variable: articles”.

I use “/news/” instead of “/blog” in my URLs.

What i try to achieve is that the user is able to select multiple tags which than filter the suggested blogposts + the H1 should change to the Tag1 Tag2 Tagx… selected by the user.

Help would be greatly appreciated=)

Blog Controller:

<?php

return function ($page, $kirby, $site, $tag, $pages) {

  // Meta
  $seo = $kirby->controller('seo' , compact('page', 'site', 'kirby'));
  
    // Override Meta Title
  $metatitle = $page->seotitle().' | '.$site->title();

  $gallery = $page->images()->sortBy('sort');

  $data = compact('gallery');
 
 // fetch the basic set of pages
  $articles = $page->children()->listed()->flip();

  // fetch all tags
  $tags = $articles->pluck('tags', ',', true);

  // add the tag filter
  if($tag = param('tag')) {
    $articles = $articles->filterBy('tags', $tag, ',');
  }

  // apply pagination
  $articles   = $articles->paginate(10);
  $pagination = $articles->pagination();
 
  $articles = compact('articles');
  $tags = compact('tags');
  $tag = compact('tag');
  $pagination = compact('pagination');
  
  return a::merge($seo, $data);

};

Blog Template:

<section class="articles">

<?php foreach($articles as $article): ?>  
	  <article>
		<a href="<?= $article->url() ?>"><?= $article->title()->html() ?></a>
		<header>
	    <?= $article->image()->crop(500, 300) ?>
 
	    <h2><?= $article->title()->html() ?></h2>
	    <p><?= $article->text()->excerpt(300) ?></p>
		</header>
		</a>
	  </article>
  <?php endforeach ?>
  
  <!-- sidebar with tagcloud -->
<aside>
  <h1>Tags</h1>
  <ul class="tags">
    <?php foreach($tags as $tag): ?>
    <li>
      <a href="<?= url($page->url(), ['params' => ['tag' => $tag]]) ?>">
        <?= html($tag) ?>
      </a>
    </li>
    <?php endforeach ?>
  </ul>
</aside>

<!-- pagination -->
<nav class="pagination">
  <?php if($pagination->hasPrevPage()): ?>
  <a href="<?= $pagination->prevPageUrl() ?>">previous posts</a>
  <?php endif ?>

  <?php if($pagination->hasNextPage()): ?>
  <a href="<?= $pagination->nextPageUrl() ?>">next posts</a>
  <?php endif ?>
</nav>

Config php:

return [
    'debug' => true,
    'schnti.cookie.link'    => 'datenschutzerklaerung',
    'panel' =>[
      'install' => true
    ],
    'routes' => [
        [
            'pattern' => 'news/tag/(:any)',
            'action'  => function ($tag) {

                return Page::factory([
                    'slug'     => $tag,
                    'template' => 'tag',
                    'model'    => 'tag',
                    'content'  => [
                        'title' => 'Results for ' . ucfirst($tag),
                    ]
                ]);

            }
        ]
     ]
    
];

Your controller needs a tweak. You need to pass through the articles, tags, and pagination in the return. To do that, you need to swap a::merge for array_merge and list those vars out in the return along with $seo and $data that you already have.

That will make the template see those variables.

On a side note, i think can probably combine those compacts into theone for $data.

Sorry for the quick replay, its late here and im too tired to mess with code for an example, but that is how to fix it.

1 Like

Thanks a lot - just tried and it works!=)

I am wondering - is it possible to also make changes to the page based on the tag selected?

  • highlight the selected tag (not sure how this should be done?)
  • change the H1 of the page (figured that one out - with <?php echo param('tag', 'Wählen deine gewünschte Kategorie aus');?>)
  • add a description tag that changes based on the selection of the tag? - is there a way to match tag + description and make it possible to edit it in the panel?

Yes.

Highlight selected tag: Check if param('tag') is the same as $tag
Description: Yes, you could, for example, define all tags in a structure field with 2 fields: name of tag and description, then fetch the descriptions from there.

Thanks a lot @texnixe, is there also the possibility to use different types of tags. For example: we want to have a page filter that enables filtering only by color, on another page we would like to filter by brand name, on a third by material? Do you have any recommendation how to set it up or could you point us to a plugin/cookbook? Many thanks

For the Highlight - tried in the controller:

      // add the tag filter
      if($tag = param('tag')) {
        $album = $album->filterBy('tags', $tag, ',');
        $classe='active';
      }

and for the template:

     <?php foreach($tags as $tag): ?>
          <a class=<?= html($classe) ?> href="<?= url($page->url(), ['params' => ['tag' => $tag]]) ?>">
            #<?= html($tag) ?>
          </a>
        <?php endforeach ?>

But that just marks all the tags with “active”. Any ideas?

Anybody?=)

You have to apply the class based on the condition that param('tag') === $tag. And yes, you can use different filters on different pages or combine multiple filters on the same page.

Do you mean like that?

if($tag = param('tag')) {
            $album = $album->filterBy('tags', $tag, ',');
            $classe ='inactive';
          }
   if( param('tag') === $tag ) {
            $classe ='active';
          }

that still gives me al tags as “active” =(

No, you have to do that in the template where you loop through the tags.

thanks a lot @texnixe this worked!=)

  <?php foreach($tags as $tag): ?>
    <a   <?php if( param('tag') === $tag ) { echo('class="active"'); } ?> href="<?= url($page->url(), ['params' => ['tag' => $tag]]) ?>">#<?= html($tag) ?></a>
    <?php endforeach ?>