Filtering entries by Taxonomy in Statamic 3 using AND or OR logic

Filtering entries by Taxonomy in Statamic 3 using AND or OR logic

Published September 4th, 2021

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.

A screenshot of the demo for AND and OR logic when querying Taxonomies in Statamic 3

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:

php 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.

<?php

namespace App\Tags;

use Statamic\Tags\Concerns;
use Statamic\Tags\Tags;

class EdiblesFilter extends Tags
{
    use Concerns\OutputsItems;
    
    /**
     * The {{ edibles_filter }} tag.
     *
     * @return array
     */
    public function index()
    {
        //
    }
}

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.

$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:

$edibles = Entry::query()
    ->where('collection', 'edibles');

To get started, let’s just return everything our query finds.

public function index()
{
    $edibles = Entry::query()
        ->where('collection', 'edibles');

    return $this->output($edibles->get());
}

Within Antlers, we can use our tag, write a loop, and see the Title of each Edible.

{{ edibles_filter }}
    {{ title }}
{{ /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

$colours = [
    'green',
    'red'
];

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:

$edibles = Entry::query()
    ->where('collection', 'edibles')
    ->whereTaxonomy('colours::green')
    ->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: 

$edibles = Entry::query()
    ->where('collection', 'edibles');

// loop the colours array, assuming we have retrieved them in to $colours
foreach ($colours as $colour) {
    $edibles = $edibles->whereTaxonomy('colours::'.$colour);
}

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:

$edibles = Entry::query()
    ->where('collection', 'edibles')
    ->whereTaxonomyIn([
        'colours::green',
        'colours::red'
    ]);

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:

$edibles = Entry::query()
    ->where('collection', 'edibles');

$inColours = []; // for our whereTaxonomyIn call

// loop the colours array, assuming we have retrieved them in to $colours
foreach ($colours as $colour) {
    $inColours[] ='colours::'.$colour;
}

$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:

public function index()
{
    $edibles = Entry::query()
        ->where('collection', 'edibles');
    
    //
    // ... all the fun query building happens here
    //

    // paginate if needed
    if ($this->params->get('paginate', false)) {
        // look for "limit", or default to 10
        $edibles = $edibles->paginate($this->params->get('limit', 10));
    } else {
        // just get the edibles
        $edibles = $edibles->get();
    }

    // output using the OutputsItems concern
    return $this->output($edibles);
}

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:

  1. Clone the repo, and configure your server as you normally would

  2. Create and configure your .env as necessary

  3. Run composer install

  4. Run npm install

  5. Build the assets with npm run production

  6. 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.

You may be interested in...