2

I want to create these kinds of filters on my Craft Commerce site:

faceted filters faceted search

I have poked around with the Faceted Navigation plugin, but haven't gotten it to work the way I want yet (posted an issue about high bandwidth usage) (Fixed in version 1.1.80), and it seems to work nicely- and it's quite fast. However, I wanted to check out 3rd party enterprise search as-a-service.

I also see there is pretty good support for a third party enterprise search service called Algolia through the Scout and Algolia plugins. I have no previous experience with Algolia, but it seems that they should be able to spit out these kinds of filters.

There also seems to be a somewhat abandonned plugin called Search Plus (which I believe stalled at Craft 2.5) and the ElasticSearch-plugin which also seems to be stalled because of a dependency on a YII2->ElasticSearch plugin.

So... should I build my own? NyStudio107 talks about using React with the GraphQL API or the Element API and Webstoemp has an example of building this using Twig.

How would you go about doing this?

2

Algolia is excellent, and I've found the scout plugin to be solid. Algolia will give you pre-built faceted navigation with instantsearch.js so there is very little development time.


Elastic search comes with the added complexity of running/configuring an ES server, will likely require on-going maintenance.


However if you have the time and don't want to rely on 3rd parties, I would recommend just DIY with twig. If you want it to refresh the listing 'live' you can use Sprig, or htmx.

Basically your filters/facets will be a form that passes a query string to your listing page. Your listing page will then check for the query string and filter accordingly.

An example from a recent project:

{# Get any URL Parameters #}

{% set keywords = craft.app.request.getParam('q')|default(null) %}
{% set kind = craft.app.request.getParam('kind')|default('*') %}
{% set order = craft.app.request.getParam('order')|default('title asc') %}

{# Run the query #}

{% set query = entries
  .search(keywords)
  .kind(kind)
  .orderBy(order)
  .limit(30)
%}

The difficulty will be the UI for sliders etc, but there are plenty of libraries for this kind of thing.

3
  • Thanks dmatthams. I didn't dive all in on Algolia yet. Do you have any examples of these kinds of searches that you have built using Algolia?
    – nitech
    Sep 24 '20 at 9:56
  • I don't have anything public, but there are examples over here: algolia.com/doc/guides/building-search-ui/widgets/showcase/js
    – dmatthams
    Sep 24 '20 at 10:14
  • Thank you. I read more about the instantsearch.js-family (which also has VueJs, Angular, React, etc versions), and they output very nice widgets.
    – nitech
    Sep 24 '20 at 11:37
2

I've done this a couple of times in Twig and a couple of times with the Faceted Nav plugin and would recommend doing it yourself in Twig, as it gives you full control. More specifically, Faceted Nav plugin cannot achieve what most people want which is to separate the filter groups with 'AND' logic while separating the individual filters in each group with 'OR' logic.

The secret to achieving this in Twig is leveraging Craft's relatedTo parameter, which is incredibly powerful and can accept multiple sets of criteria. Here's a simplified example:

{% set facet1 = craft.entries.slug(['slug-1','slug-2']).ids() %}
{% set facet2 = craft.entries.slug(['slug-3','slug-4']).ids() %}
{% set facet3 = craft.categories.slug(['slug-5','slug-6']).ids() %}

{% set params = ['and',
    { targetElement: facet1, field: 'facetFieldHandle_1' },
    { targetElement: facet2, field: 'facetFieldHandle_2' },
    { targetElement: facet3, field: 'facetFieldHandle_3' }
] %}

{% set entries = craft.entries.section('products').relatedTo(params).all() %}

<ul>
    {% for entry in entries %}
        <li>{{ entry.title }}</li>
    {% endfor %}
</ul>

...So you can see there that my facet1, facet2, facet3 arrays are basically acting like 'OR' filters ("fetch me the entries with this slug OR this slug OR this slug"), then we feed those arrays into the relatedTo parameter prefixed with the 'AND' keyword. (Naturally, those hardcoded slug-1, slug-2 values will need to come from your form data - usually best to take them via query string params so that the result pages are URL-addressable e.g. craft.app.request.getParam('facet1')).

Once you start adding logic to pre-check the right checkboxes and adding eager loading etc, it gets more complicated, but that's the basics. The last time I did this I added some simple JS to enhance/prettify the URL query strings (by default a form submission will append multiple values repeatedly like this ?facet1[]=abc&facet1[]=def&facet2[]=123 -- which gets quite ugly and long). With a little JS you can transform into something more like ?facet1=abc|def&facet2=123, and use Twig's is iterable test to check whether you're dealing with the enhanced version or the default/no-js/slow-js/googlebot version and split('|') the data accordingly.

4
  • Did you ever consider or try out Algolia or similar "enterprise search as-a-service" providers?
    – nitech
    Sep 29 '20 at 13:02
  • Yes, looked at Algolia pretty extensively and in the end it just seemed overly complicated for something I could build in Twig very easily. I was also massively put off by their pushy sales rep who literally kept calling every few days to see if I was ready to sign up. Sep 29 '20 at 13:19
  • Ah... I understand. My idea was to use a third party search service so I could leave AI and smart search result adaptation to them. Could I ask how many products your sites have?
    – nitech
    Sep 29 '20 at 13:22
  • One I'm working on at the moment which is using this kind of filtering has 4000 Commerce products with 11,000 variants, and the filtering is working nicely. Sep 29 '20 at 14:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.