A Craft CMS plugin for using URL segments as filtering criteria on an entry query.
Craft makes it straightforward to declare dynamic routes as regular expressions and redirect them to be handled by a template. However, the templates themselves remain dumb handlers. They may optionally be passed on some context in the form of named parameters but they have to do the heavy lifting of building the data set required for rendering the page.
This may not be a problem for pages with one or two variables,
like a blog's year and month archives (e.g. blog/2015/01
).
The template would fetch the list of posts from craft.entries
and narrow the range
depending on if the year
and month
variables are set.
But what if the blog also added a category page (e.g. blog/camping
)?
And what if the category pages supported their own yearly and monthly archive pages
(e.g. blog/camping/2014
)? We would either end up duplicating the code to fetch posts
by creating multiple copies of the archive template, or end up adding the logic to handle category,
year, and month filters all in a single template and increasing its overall complexity.
The Router plugin attempts to solve this problem by taking on the job of filtering entries
based on URL parameters. It adds a new template variable entries
which can be configured to,
for the URL blog/2015/01
contain blog posts published in January 2015, or for the URL
blog/camping/2014
to show blog posts published in 2014 under the category "Camping".
We recorded a video (36 mins) about the plugin for Straight Up Craft. It talks about the problems that Router is trying to solve and includes a step-by-step tutorial + demo about using the plugin on the Craft Blog Starter project.
In order to create URL rules that automatically build an Entry Query based on the URL,
you will need to create a router.php
file in your config folder, adjacent to your existing
routes.php
file.
<?php
/* config/router.php */
return [
'rules' => [
// URI pattern with named subpatterns
'<section:blog>' => [
'segments' => [
'<year:\d{4}>',
'<category:{slug}>',
'in:<location:{slug}>',
],
// array of filters that are activated when
// the key matches a subpattern variable declared in
// the route's regular expression
'criteria' => [
// Restrict entries to the selected section
'section' => [
'type' => 'section',
],
// Filter entries by year
'year' => [
'type' => 'year',
'field' => 'postDate',
],
// Filter entries by related category
// from the category group with the handle 'travel-styles'
'category' => [
'type' => 'category',
'group' => 'travel-styles',
],
// Filter entries by related entry
// from the section with the handle 'locations'
'location' => [
'type' => 'entry',
'section' => 'locations',
],
],
// template file
'template' => 'blog/_archive',
],
],
];
Each rule expects the following parameters:
Default: []
An array of optional URL segment rules. Example:
'segments' => [
'<year:\d{4}>',
'<category:{slug}>', // eg. budget, luxury, cruise, urban
'in:<location:{slug}>', // eg. asia, europe, australia
]
/*
This will match the following URL suffixes
…/2019
…/2019/budget
…/2019/budget/in:asia
…/2019/in:asia
…/budget
…/budget/in:asia
…/in:asia
Order is relevant, so it will *not* match the following URLs
…/budget/2019
…/in:asia/budget
…/2019/in:asia/budget
*/
Default: true
Enables/disables support for URLs with segment combinations. When disabled,
only one segment match will be allowed per URL. Example:
'combineSegments' => false
/*
This will match only the following URL suffixes
…/2019
…/budget
…/in:asia
It will *not* match the following URLs
…/2019/budget
…/2019/budget/in:asia
…/2019/in:asia
…/budget/in:asia
*/
Default: []
An array of filters for the Entry Query. Example:
'criteria' => [
'year' => [ 'type' => 'year', 'field' => 'postDate' ],
'category' => [ 'type' => 'category', 'group' => 'tripCategories' ],
'location' => [ 'type' => 'entry', 'section' => 'locations' ],
]
Default: ''
The template path used to render the request. Example:
'template' => 'blog/_archive'
A filter is activated when the corresponding trigger key (named parameter)
is present in the route. Based on the type of filter, a set of conditions (criteria)
are added to an Entry Query object. This is repeated for every activated filter,
and the resulting Entry Query is passed on to the template as the entries
variable.
The plugin currently supports the following different types of filters:
Adds a relatedTo
criteria to the Category with the given slug, and any of its
descendants. The Category’s search can be scoped by specifying a Category Group handle
in the optional param group
. The relation’s field can be specified using the optional
param field
. Set the filter’s includeDescendants
to false if you do not wish descendant
Categories to be included in the relatedTo
criteria.
Similar to the category
filter but supports a comma separated list of slugs instead of
just one slug.
Adds a relatedTo
criteria to the Entry with the given slug, and any of its descendants.
The Entry’s search can be scoped by specifying a Section handle in the optional param
section
. The relation’s field can be specified using the optional param field
. Set the
filter’s includeDescendants
to false if you do not wish descendant Entries to be included
in the relatedTo
criteria.
Similar to the entry
filter but supports a comma separated list of slugs instead of just
one slug.
Adds a field criteria to the field specified by handle
(required param).
Adds a numeric month criteria on the optional param field
(which defaults to postDate
).
Warning
For this filter to work on custom (user-defined) fields in Craft 5, either the section
or type
/types
filter must be applied before the month
filter.
Adds a search
criteria. Criteria value can be overidden using the optional
param value
.
Adds a section
criteria if the specified Section handle is valid. Section handle
value can be overidden using the optional param value
.
Adds a relatedTo
criteria to the Tag with the given slug. The Tag’s search can be
scoped by specifying a Tag Group handle in the optional param group
. The relation’s field
can be specified using the optional param field
.
Similar to the tag
filter but supports a comma separated list of slugs instead of
just one slug.
Adds a type
criteria if the specified EntryType handle is valid. EntryType handle
value can be overidden using the optional param value
.
Note
The named URL parameter is set to an EntryType object in Craft 5 (as expected), but an EntryType[] array in Craft 3 and 4. This is because entry type handles are not unique (across sections) in older versions of Craft. You can override this behaviour by setting the filter's assumeUniqueHandles
optional param to true
. This parameter has no effect in Craft 5.
Similar to the type
filter but supports a comma separated list of handles instead of
just one handle.
Adds a relatedTo criteria to the entry with the given URI, and any of its descendants. The
Entry’s search can be scoped by specifying a Section handle in the optional param
section
. The relation's field can be specified using the optional param field
. Set the
filter's includeDescendants
to false if you do not wish descendant Entries to be included
in the relatedTo
criteria.
Similar to the uri
filter but supports a comma separated list of URIs instead of just
one URI.
Adds a date range criteria for the given year on optional param field
(which defaults
to postDate
).
You can install this plugin from the Plugin Store or with Composer.
Go to the Plugin Store in your project’s Control Panel and search for “Router”. Then click on the “Install” button in its modal window.
Open your terminal and run the following commands:
# go to the project directory
cd /path/to/project
# tell composer to use the plugin
composer require miranj/craft-router
# tell Craft to install the plugin
./craft plugin/install router
This plugin requires Craft CMS 3, 4, or 5. The Craft 2 version is availabe in the v0
branch.
Brought to you by Miranj