Filtering via routes
The target
The standard way to filter (page) collections by tags or categories etc. is by using parameters in your URL. A typical URL usually looks like this: http://example.com/blog/tag:travel
.
When a website visitor opens this link in the browser, the controller checks if the URL contains a parameter or not and returns the corresponding collection. You can find an example in the "Filtering with tags" recipe.
While this works perfectly fine, some people prefer URLs like http://example.com/blog/travel
without the parameter separator or even without the parameter name, i.e. http://example.com/blog/tag/travel
.
How can we achieve this? Luckily, Kirby comes with a powerful router, which we can leverage for this purpose.
Let's go through this step by step.
The route
First, we need a route that listens to our URL pattern.
The pattern blog/tag/(:any)
with the (:any)
placeholder for the actual value will listen to URLs like blog/tag/travel
.
Within the action
closure, we render the blog
page and send the value for our tag in the methods's parameter array.
The actual filtering logic takes place in the Controller.
The controller
By loading the $tag
variable to our anonymous function, we can fetch the variable we passed to the render()
function in the route.
With…
…we check if the $tag
variable is set and if so, we filter the articles by the tag's value, and finally return the $articles
variable to the template.
In your template, you can now loop through the $articles
collection and render the HTML for each article.
In case you use multi-word tags with spaces or special characters, you have to urlencode()
your tags in your tag links and urldecode()
them again in the controller.
A modified route
If you want to leave out the tag
bit in the URL, you have to modify the route a bit:
In this case, tag values and subpages might conflict. First we therefore check if there is a subpage and return it if it exists. If not, we handle the value as tag and render the blog
page as in the route above.
Using a different template
In the examples above, we always return the blog page/template, only with a filtered set of subpages. If we want to use a different template for our tags or categories, we have to change our route and we need a new controller and template:
Route
Within our route, this time we return a virtual page using the Page::factory()
method, because the page doesn't exist in the file system.
Controller
Here we use the urldecode()
d page slug as our filter value.