Happy New Year!
I am usually the guy trying to adapt other peoples ideas so I can learn how to do my task. I usually take from this forum, not give. Here’s the first move to change that. I’m not a php
programmer, so delving into some of this within Kirby can be daunting.
Please let me know if you have any improvements to this task or errors I’ve made, so that others don’t get it wrong.
After spending a lot of time adapting the suggested filtering options within Kirby, I’ve tried to make an example of what I’m using to filter pages based on a select
field in children page blueprints.
You can find the recipe from Kirby that this task was based on here:
A few caveats, first:
- We want Pretty URLs, not colons or semicolons or
?
parameters, etc. - If you require multiple filters, you’ll be going down your own path. This is not your solution
- The code I’m writing here is general so you can reference it for filtering any children items
- Of course, to adapt this to Blog Posts, you’d need to change your object names to reflect this ($items could be $posts, $products, $features, $albums, $recipes, etc.), filters could be ‘tags’, ‘categories’, ‘sizes’, etc.
Now we’re going to go through all of the parts of how to do this in a general way so you can get this working in any section you want to filter, using Pretty URLs, showing item counts, create a filter link list for filtering, and adding aria attributes used to also highlight the current filter.
Example:
We’re not going to use the built-in tags
feature of Kirby, but we will create our own using the select
field with defined options. We’ll do this in three steps.
First, the Controller
First, create a controller for your page, the page that will show filtering for the children of this page, let’s use a Blog as an example:
(More on Controllers: Controllers | Kirby CMS, or this video: https://youtu.be/Im2bx2WytPc)
site/controller/blog.php
<?php
return function ($page, $filter = null) {
// All items, unfiltered
$items = $page->children()->listed();
$tags = [];
// Assuming all items use the same blueprint, get the 'tags' field from the first item
if ($firstItem = $items->first()) {
$itemBlueprint = $firstItem->blueprint();
$options = $itemBlueprint->field('tags')['options'] ?? [];
// Build an array with the correct keys and counts
foreach ($options as $key => $value) {
$tags[$key] = [
'label' => $value,
'count' => $items->filterBy('tags', $key, ',')->count()
];
}
}
// Filtered items based on the 'filter' parameter
$filteredItems = $filter ? $items->filterBy('tags', $filter, ',') : $items;
return [
'items' => $items, // Keep all items here
'filteredItems' => $filteredItems, // Apply the filter if needed
'tags' => $tags,
'activeTag' => $filter
];
};
Again, change the object names, et al, to reflect what sort of items you’re filtering if you like. Just match these up with the other places these names are used in the code that comes next.
Second: the template
Let’s make a filter link list of items, and then we’ll add in a loop to show the items that have been filtered. The link list should look like this:
All (11), Wetsuits (5), Surfboards (1), Fin Systems (2), Snowboards (1), Hardware (1), Boots (1)
Let’s make it:
/site/templates/blog.php
<p class=“filters”>
<a href="<?= $page->url() ?>"
aria-current="<?= is_null($activeTag) ? 'page' : 'false' ?>">
All (<?= $items->count() ?>)
</a>
<?php foreach ($tags as $key => $tag): ?>
<?php $isActive = $activeTag === $key; ?>
<a href="<?= url($page->url() . '/tag/' . urlencode($key)) ?>"
aria-current="<?= $isActive ? 'page' : 'false' ?>">
<?= $tag['label'] ?>
(<?= $tag['count'] ?>)
</a>,
<?php endforeach; ?>
</p>
<div class="items">
<?php foreach ($filteredItems as $item): ?>
<div class="item">
<h2><?= $item->title()->html() ?></h2>
<!-- Item details -->
</div>
<?php endforeach; ?>
</div>
Note: Make sure that your foreach
loop for the items (Blog Posts) is using the ‘filteredItems’ object, and not the object that has all of your items (Blog Posts).
Third, the Routes
Next we’ll add our Routes in the site config files. If you have multiple of these for different environments, make sure to add it to the ones for production where you intend to use this.
Knowing that the syntax of the config.php file is important, if you did this part and it doesn’t work for you, make sure to check all of your opening and closing brackets and general PHP syntax.
Okay, let’s instruct Kirby to look at the URL parameter that has the filter we need to sort the items.
/site/config/config.php
<?php
// for more, see: https://getkirby.com/docs/reference/system/options
return [
'routes' => [
[
'pattern' => 'section/tag/(:any)',
'action' => function ($filter) {
return page('section')->render([
'filter' => $filter
]);
}
],
// ... other routes
],
// ... other configurations
];
Now just alter this to use blog/tag/(:any)
instead of section/tag/(:any)
or whichever section of your site this is used.
Then also change page('section')
to also be the parent page of the filtered items, so for Blog it would be page('blog')
, for example.
Lastly, make sure your blueprint for the items have a select
field with defined options like this:
fields:
tags:
label: Tags
type: select
options:
tshirt: T-Shirt
hoodie: Hoodie
// ... replace these with other options for your items, like tags, sizes, kinds, museum names, type of clothes, etc.
// NOTE: the first string should have no spaces: 'tshirt', not 't shirt' or 't-shirt'.
So, that’s it! Now your children items filtering will have URLs like these:
store.shop/products/kind/surfboards
museum.museum/artwork/medium/oil
blog.blog/blog/topic/php
restaurant.eat/menu/time/lunch
instead of:
store.shop/products/kind:surfboards
museum.museum/artwork/?medium=oil
blog.blog/blog/topic;windows
…etc.
Of course, the Kirby cookbook recipe lists many more ways to filter children, and for that you should read up and understand the ideas shared there.
Lastly, if you have fixes, edits, or ideas, please reply. Have a great new year!