Filtering entries by Taxonomy in Statamic 3 using AND or OR logic
Important: The information in this article is over 12 months old, and may be out of date or no longer relevant.
But hey, you're here anyway, so give it a read see if it still applies to you.
This week I have been working on a client’s site build in Statamic 3, and they have a very specific need regarding filtering content.
They have a Collection of Entries that can be filtered by multiple Taxonomies. Straight forward enough. But they also need to be able to filter on multiple Terms within the one Taxonomy, and also have some behaviour with AND logic, and others with OR logic.
I know that sounds totally confusing and illogical, but for their use case, it does make sense, so I’ll allow it.
This got me exploring the Collection Tag and Filtering to see if I can just use the existing Collection tag that Statamic gives us but given the scale of the query, I actually thought it’d be easier (in the long run) to have my own Tag for the project that handles their specific filtering needs.
So what’s the solution? Basically it’s a:
custom Tag for the project,
that has the same output behaviour as the Collection tag
but runs its own Query internally
That in itself is fairly straight forward – but the combination of AND and OR logic took a little bit of exploring to figure out how to achieve this.
Typically I’ve been using filtering and taxonomies for single Terms – i.e. show me all posts with “This Tag”. But it’s when you need to do something more that your nerdy developer hat comes on. But damn it fits so well.
TL;DR: check out the demo repo on Github for a working example you can run.
In the demo, we have a Collection of Edibles, which each have one or more Colour Terms attached. You’re able to select one or more Colours using the checkboxes, and then see the difference in behaviour when querying multiple Terms using AND and OR logic.
Its purpose is to help illustrate the differences of the logic side-by-side. And who doesn't love eating healthy. Wait... maybe I should have put fries on this.
Make a Tag
Tags are such an easy way to build your own functionality (and have PHP access) for the front end. They’re fast to make, logical, and project specific. Make a Tag by asking please
:
1php please make:tag EdiblesFilter
This is where we will build the filter logic. To get started, we will firstly use the OutputsItems
concern created by Statamic – this makes it easy for our tag to output and paginate, just like the Collection tag for example.
1<?php 2 3namespace App\Tags; 4 5use Statamic\Tags\Concerns; 6use Statamic\Tags\Tags; 7 8class EdiblesFilter extends Tags 9{10 use Concerns\OutputsItems;11 12 /**13 * The {{ edibles_filter }} tag.14 *15 * @return array16 */17 public function index()18 {19 //20 }21}
Build a Query
Statamic comes with a number of Query Builders for different entities within the Statamic ecosystem – we’re wanting to build our query for Entries.
1$edibles = Entry::query();
For our example, we want to return Entries from our Edibles Collection – so we’ll set our collection to look at our “edibles” collection only:
1$edibles = Entry::query()2 ->where('collection', 'edibles');
To get started, let’s just return everything our query finds.
1public function index()2{3 $edibles = Entry::query()4 ->where('collection', 'edibles');5 6 return $this->output($edibles->get());7}
Within Antlers, we can use our tag, write a loop, and see the Title of each Edible.
1{{ edibles_filter }}2 {{ title }}3{{ /edibles_filter }}
Querying Taxonomies: AND
With the Entry Query Builder in Statamic 3, we have access to two methods:
whereTaxonomy
whereTaxonomyIn
These two methods allow us to use AND or OR logic when building our query. Let’s start with AND logic – it’s the most straight forward, and for most cases probably the most logical.
In this example, we’re expecting to filter based on our Colours terms – and these will come to us as an array via the query string.
/?colours[]=green&colours[]=red
When we get our params to the $colours variable, we would have
1$colours = [2 'green',3 'red'4];
The general idea is that we need to add each of these colours to our Query Builder. In Laravel and Eloquent, we have where
and orWhere
to help us build logic – we use where
for AND logic, and orWhere
for OR logic. The same rules apply here (although Statamic doesn’t give us “or” method variants).
This means that when we add multiple whereTaxonomy
calls to our query, they’re being added with AND logic.
If you’re new to whereTaxonomy
, when querying with whereTaxonomy
, we pass a string made up of a Taxonomy handle, i.e. “colours”, followed by the Term we want to query, i.e. “red”, such as:
colours::red
Adding this to our Query Builder, if we wanted to filter based on ‘green’ AND ‘red’, our query would look like:
1$edibles = Entry::query()2 ->where('collection', 'edibles')3 ->whereTaxonomy('colours::green')4 ->whereTaxonomy('colours::red');
Basically we’re saying:
get me entries
from the “edibles” collection
that have the ‘green’ term, and
have the ‘red’ term
Given we have our colours coming via the query string, we can loop through these instead to achieve the same result:
1$edibles = Entry::query()2 ->where('collection', 'edibles');3 4// loop the colours array, assuming we have retrieved them in to $colours5foreach ($colours as $colour) {6 $edibles = $edibles->whereTaxonomy('colours::'.$colour);7}
Make sense so far? Don’t forget to check out the EdibleFilters Tag code in the Github repo for a working example.
Querying Taxonomies: OR
Let’s now look at whereTaxonomyIn
and how we can achieve the OR logic.
Similar to Statamic’s whereIn
, we are looking to match acceptable (or allowed) terms – in other words, our value must be one of a set of provided values.
whereTaxonomyIn
is very similar – we provide an array of allowed Terms, and the Query Builder matches when any of them are found – in other words, OR.
Think back to ‘green’ and ‘red’ – with OR logic and whereTaxonomyIn
, our query becomes:
1$edibles = Entry::query()2 ->where('collection', 'edibles')3 ->whereTaxonomyIn([4 'colours::green',5 'colours::red'6 ]);
Here we’re saying:
get me entries
from the “edibles” collection
where the taxonomy ‘colours’ has any of ‘green’ or ‘red’
We can update our Tag to build this query based on the query string:
1$edibles = Entry::query() 2 ->where('collection', 'edibles'); 3 4$inColours = []; // for our whereTaxonomyIn call 5 6// loop the colours array, assuming we have retrieved them in to $colours 7foreach ($colours as $colour) { 8 $inColours[] ='colours::'.$colour; 9}10 11$edibles = $edibles->whereTaxonomyIn($inColours);
Want to see the finished Tag? Check out the EdiblesFilter tag in the demo repo.
Put it together
We now know that:
multiple
whereTaxonomy
calls behaves as AND,a
whereTaxonomyIn
call behaves as OR
You can use this logic in your own tags to even combine behaviour so that one Taxonomy could be an AND logic and a different Taxonomy could be an OR logic – obviously depends of if that makes logical sense for your site.
For my client’s site, I’ve got my Tag querying four different Taxonomies with a combination of AND and OR logic (based on the client’s requirements) – but the really cool part: using Statamic and its incredible tools, I only needed to write a few dozen lines of code. The OutputsItems
concern makes it effortless to output items from the tag and the Query Builder for Entries gives you a familiar Laravel-esque builder experience to help build your own logic. Understanding the difference and behaviour of whereTaxonomy
and whereTaxonomyIn
is the biggest hurdle – with that under your belt, you’re all set to build your own awesome queries.
Bonus: pagination
You may also want your Tag to have pagination. After all, if you have hundreds of Entries, you probably don’t want them all shown at once.
Instead of just getting our query’s items, like Laravel, Statamic gives us a paginate method for its query builders:
1public function index() 2{ 3 $edibles = Entry::query() 4 ->where('collection', 'edibles'); 5 6 // 7 // ... all the fun query building happens here 8 // 9 10 // paginate if needed11 if ($this->params->get('paginate', false)) {12 // look for "limit", or default to 1013 $edibles = $edibles->paginate($this->params->get('limit', 10));14 } else {15 // just get the edibles16 $edibles = $edibles->get();17 }18 19 // output using the OutputsItems concern20 return $this->output($edibles);21}
Within your template, you can then use pagination just like you would with the Collection tag – so don’t forget to check out the Collection Tag Pagination docs.
Explore the demo code
You can get working demo code of this sort of thing from the demo repo on Github. If you’re looking this far, I’m assuming you’re switched on with Statamic 3. With that in mind:
Clone the repo, and configure your server as you normally would
Create and configure your
.env
as necessaryRun
composer install
Run
npm install
Build the assets with
npm run production
If you want to log in to the Control Panel, make yourself a user with
php please make:user
Visit the site in your browser, and you can use the checkboxes to select different colours, and when you filter, see the different behaviour of the AND and OR logic.
And of course, given you have the code, don’t forget to check out the Tag for the construction of the query.
The ease at which Statamic can be extended makes it a developer’s dream – and the use of Tags and Statamic’s awesome Query Builders allow you to build sites that go beyond the basics. While filtering with OR logic may not make sense for every site, there are cases where it logically makes sense – and hopefully you have a greater understanding of whereTaxonomy
and whereTaxonomyIn
to help build and shape your own logic behaviour.