Fat Buddha Designs

Affordable, Creative, Hand-Crafted Websites

Filtering Content With Kirby CMS

Kirby logo

Published:

Reading Time: (approx) 3 min

Tags that this post has been filed under.

The main content about Filtering Content With Kirby CMS

Filtering Content From Structure Data

I have recently moved a website from Perch CMS to Kirby CMS. This proved relatively easy but one area which took a little figuring out was filtering data that was produced from a structure field in the CMS system. The client wanted to frontend content, events in this case, to be able to be filtered by a type and also by month.

Kirby is very flexible and allows data to be input in the backend in numerous ways but for this particular client I choose a structure layout.

I had produced a blueprint which would produce structure type field, this would allow the client to add event types as and when the need arises for something new, this is done with the use of a tag field.

Blueprint

This is the blueprint for the events page stored in site/blueprints/pages:

title: Events Page
icon: 📝
present: page

tabs:
  content:
    columns:
    label: Page Content
    fields:
      pageHeadline:
        label: Page Headline
        type: text
        width: 1/2
      eventCategories:
        label: Categories that events can be filtered by.
        type: tags
        sort: asc
      eventMonths:
        label: Months that events can be filtered by.
        type: tags
        sort: asc
      events:
        type: structure
        label: Upcoming Events
        sortBy: startdate desc
        fields:
          title:
            label: Event Title
            type: textarea
          text:
            label: Event Text
            editor:
            type: markdown
            font: sans-serif
            size: medium
          startDate:
            label: Event Start Date
            type: date
            display: DD/MM/YYYY
            width: 1/2
          finishDate:
            label: Event Finish Date
            type: date
            display: DD/MM/YYYY
            width: 1/2
          startTime:
            label: Event Start Time
            type: time
            display: HH:mm
            width: 1/2
            step:
              unit: minute
              size: 30
          finishTime:
            label: Event Finish Time
            type: time
            display: HH:mm
            width: 1/2
            step:
              unit: minute
              size: 30
          cost:
            label: Event Cost (No £ symbol)
            type: text
            width: 1/3
          category:
            label: Type of Event
            type: multiselect
            width: 1/3
            sort: asc
            options:
              type: query
              query: page.eventCategories.split
          month:
            label: Month Event Takes Place
            type: multiselect
            width: 1/3
            options:
              type: query
              query: page.eventMonths.split

Once the data is entered in the backend, it’s then a case of extracting and displaying it.

To do this I needed to create four files, this helps to keep the final events.php file ‘dry’ and makes the code easier to follow.

Collections

First up I created a collection file called events.php and stored in site/collections

<?php

return function ($site) {
  return $site->find('events')
  ->events()
  ->toStructure()
  ->filter(function ($child) {
    return $child->startdate()->toDate() > time() && $child->enddate()->toDate() < time();
  })
  ->flip();
};

This searches through the $site, returning the all events called events in Structure format, and then filters them to only display any that events that have a future date, lastly these are flipped in order.

Controller

Next up it was time to create a controller file called events.php and stored in site/controllers

<?php

return function($page) {

  $unfiltered         = kirby()->collection('events');
  $categoryFilters    = get('category');
  $monthFilters       = get('month');
  $eventCategories    = $unfiltered->pluck('category', ',', true);
  $eventMonths        = $unfiltered->pluck('month', ',', true);

  // filter conditionally
  $unfiltered = $unfiltered
  ->when($categoryFilters, function ($categoryFilters) {
    return $this->filterBy('category', $categoryFilters, ',');
  })
  ->when($monthFilters, function ($monthFilters) {
    return $this->filterBy('month', $monthFilters, ',');
  });

  $eventCategories    = $unfiltered->pluck('category', ',', true);
  $eventMonths        = $unfiltered->pluck('month', ',', true);

  return [
    'unfiltered'        => $unfiltered,
    'categoryFilters'   => $categoryFilters,
    'monthFilters'      => $monthFilters,
    'eventCategories'   => $eventCategories,
    'eventMonths'       => $eventMonths
  ];
};

Here we take the events collection and assign it to $unfiltered(you can use any name you want here), we then assign the name from each of our selects to $categoryFilters and $monthFilters respectively and then lastly we assign $eventCategories and $eventMonths by plucking the values from the collection.

This section does all the work, using the when() function to check each selects 'GET' value against the plucked values:

  $unfiltered = $unfiltered
  ->when($categoryFilters, function ($categoryFilters) {
    return $this->filterBy('category', $categoryFilters, ',');
  })
  ->when($monthFilters, function ($monthFilters) {
    return $this->filterBy('month', $monthFilters, ',');
  });

And after this we have to return each of our used variables like this -

  return [
    'unfiltered'        => $unfiltered,
    'categoryFilters'   => $categoryFilters,
    'monthFilters'      => $monthFilters,
    'eventCategories'   => $eventCategories,
    'eventMonths'       => $eventMonths
  ];

Snippet

In my case I then created a filter block file saved as a snippet and stored in site/snippets.

This is the two selects that I want to use for filtering on the events page, one for the event type and one for the month the event takes place.

<form id="filters" method="GET">
  <div>
  <p>Choose a type of Event:</p>
  <select name="category" onchange="this.form.submit()">
    <?php foreach($page->eventCategories()->split() as $item): ?>
      <option<?php e(isset($data['categoryFilters']) && $data['categoryFilters'] == $item, ' selected') ?> value="<?= $item ?>"><?= html($item) ?></option>
    <?php endforeach ?>
  </select>
  </div>
  <div>
  <p>Select a Month</p>
  <select name="month" onchange="this.form.submit()">
    <?php foreach($page->eventMonths()->split() as $item): ?>
      <option<?php e(isset($data['monthFilters']) && $data['monthFilters'] == $item, ' selected') ?> value="<?= $item ?>"><?= html($item) ?></option>
    <?php endforeach ?>
  </select>
  </div>
</form>

Template

And then lastly the events.php file, which is stored in site/templates.

This lists all the events in the collection that have a future date, we can then filter by event type or/and month using the selects.

<main id="main">
<section>
  <p>Below are all the upcoming events.</p>

  <?php snippet('filter-block-events') ?>

    <?php foreach ($unfiltered as $item): ?>
    <div>
      <p><?= $item->title() ?></p>
      <p><?= $item->startdate()->toDate('D, F j Y') ?><?php if ($item->finishdate()->toDate('D, F j Y')): ?>to<?= $item->finishdate()->toDate('D, F j Y') ?><?php endif ?></p>
      <p><?= $item->starttime()->toDate('g:i a'); ?> to <?= $item->finishtime()->toDate('g:i a'); ?></p>
      <p><?= $item->cost() ?></p>
      <?= $item->text()->kirbytext() ?>
    </div>
    <?php endforeach ?>
  </section>
</main>

You can see the final filtering in action here :- Kirby Filtering.

If you are looking for resources on Kirby filtering, there are several examples of filtering on the Kirby website, which is really useful.

Journal Index

Previous Article: How Green Is Your Website?

Next Article: SCSS Processing With Eleventy