1

Been scratching my head for the past day on how to implement search with multiple categories.

At the moment, I have product categories as a select dropdown which only allows to search in one category at a time. This is passing the value as a parameter. How do I go about searching with multiple categories (checkboxes) at once? Here's what I have so far.

Any ideas?

search.html

<form action="{{ url('food-suppliers/results') }}">
    <input type="search" name="query" placeholder="What are you looking for?" style="width: 100%;">

    <select name="supplier">
      <option value="">All Markets</option>
      {% set suppliers = craft.categories.group('suppliers') %}
      {% for supplier in suppliers %}
      <option value="{{ supplier.slug }}">{{ supplier.title }}</option>
      {% endfor %}
    </select>

    <select name="product">
      <option value="">All Products</option>
      {% set products = craft.categories.group('product') %}
      {% for product in products %}
      <option value="{{ product.slug }}">{{ product.title }}</option>
      {% endfor %}
    </select>

    <select name="delivery">
      <option value="">All Delivery</option>
      {% set deliveries = craft.categories.group('delivery') %}
      {% for delivery in deliveries %}
      <option value="{{ delivery.slug }}">{{ delivery.title }}</option>
      {% endfor %}
    </select>

    <div class="">
      <input type="checkbox" name="homeGrown" value="true">Home Grown<br/>
      <input type="checkbox" name="preparedProduce" value="true">Prepared Produce<br/>
      <input type="checkbox" name="redTractor" value="true">Red Tractor<br/>
    </div>

    <div class="">
      <input type="submit" value="Search">
    </div>

</form>

results.html

{% set query = craft.request.getParam('q') %}
{% set supplier = craft.request.getParam('supplier') %}
{% set product = craft.request.getParam('product') %}
{% set delivery = craft.request.getParam('delivery') %}

{% set homeGrownParam = craft.request.getParam('homeGrown') %}
{% set preparedProduceParam = craft.request.getParam('preparedProduce') %}
{% set redTractorParam = craft.request.getParam('redTractor') %}


{% set supplierCat = craft.categories.group('supplier').slug(supplier).first %}
{% set productCat = craft.categories.group('product').slug(product).first %}
{% set deliveryCat = craft.categories.group('delivery').slug(delivery).first %}


{% set supplierParam = supplier ? {targetElement: supplierCat} : '' %}
{% set productParam = product ? {targetElement: productCat} : '' %}
{% set deliveryParam = delivery ? {targetElement: deliveryCat} : '' %}


{% set entries = craft.entries.section('suppliers').relatedTo(productParam).relatedTo(deliveryParam).search(query).homeGrown(homeGrownParam).preparedProduce(preparedProduceParam).redTractor(redTractorParam) %}
2

Before I answer your main question, I'm seeing a couple issues with what you’re currently doing that are worth correcting.

First, this code:

{% set supplierCat = craft.categories.group('supplier').slug(supplier).first %}
{% set productCat = craft.categories.group('product').slug(product).first %}
{% set deliveryCat = craft.categories.group('delivery').slug(delivery).first %}

should be replaced with:

{% set supplierCat = supplier ? craft.categories.group('supplier').slug(supplier).first %}
{% set productCat = product ? craft.categories.group('product').slug(product).first %}
{% set deliveryCat = delivery ? craft.categories.group('delivery').slug(delivery).first %}

in case any of the supplier/product/delivery params are blank.

Next, this code:

{% set supplierParam = supplier ? {targetElement: supplierCat} : '' %}
{% set productParam = product ? {targetElement: productCat} : '' %}
{% set deliveryParam = delivery ? {targetElement: deliveryCat} : '' %}

should be replaced with:

{% set supplierParam = supplierCat ? {targetElement: supplierCat} %}
{% set productParam = productCat ? {targetElement: productCat} %}
{% set deliveryParam = deliveryCat ? {targetElement: deliveryCat} %}

That way you will avoid unexpected behavior if a supplier/product/delivery was posted, but it had an invalid slug, and yielded no results. Also, the : ''s at the end are just not really necessary unless your really wanted them to be strings by default (no reason in this case though), as Twig will default the values to null, which is just as good.

Also, you can’t chain multiple relatedTo params together like this:

.relatedTo(productParam).relatedTo(deliveryParam)

When you do that, the second relatedTo param will just override the 1st one.

Instead you’d need to do something like:

{% set relationParams = [supplierParam, productParam, deliveryParam]|filter %}

{% set entries = craft.entries({
    section: 'suppliers',
    relatedTo: relationParams ? ['and']|merge(relationParams) : null,
    search: query,
    homeGrown: homeGrownParam,
    preparedProduce: preparedProduceParam,
    redTractor: redTractorParam
}) %}

(I’m using the {p1: v1, p2: v2} syntax for setting the criteria params here for clarity, but you can use the p1(v1).p2(v2) chaining syntax instead if you prefer it.)


With that out of the way…

To support searching for entries that are related to multiple suppliers/products/deliveries at the same time, the first thing you’ll need to do is update your search form. I know you said you want to go with checkboxes, but for simplicity’s sake I’ll demonstrate with multi-select boxes, since that’s closer to your original code and produces the same request param data.

For each of your <select>s, just append a [] to the input name and add the multiple attribute:

<select name="supplier[]" multiple>
...
<select name="product[]" multiple>
...
<select name="delivery[]" multiple>
...

In your search results template, change .first to .find() when fetching the categories:

{% set supplierCat = supplier ? craft.categories.group('supplier').slug(supplier).find() %}
{% set productCat = product ? craft.categories.group('product').slug(product).find() %}
{% set deliveryCat = delivery ? craft.categories.group('delivery').slug(delivery).find() %}

And assuming you want this to be an inclusive search (where only results with all of the selected suppliers should be included, etc.), you’ll need to prepend 'and' to each of the targetElement arrays:

{% set supplierParam = supplierCat ? {targetElement: ['and']|merge(supplierCat)}l %}
{% set productParam = productCat ? {targetElement: ['and']|merge(productCat)} %}
{% set deliveryParam = deliveryCat ? {targetElement: ['and']|merge(deliveryCat)} %}

That’s it. The rest can stay the same.

  • There's a letter "l" at the end of this {% set supplierParam = supplierCat ? {targetElement: supplierCat}l %}, was that intended? – Jose Mar 3 '17 at 15:31
  • Works great! I just had to add a selected attribute to the first option in the select dropdown block. So the code looks something like this: <option value="" selected>All Products</option> – Jose Mar 3 '17 at 15:34
  • Interestingly, if I don't have the selected attribute on the first option of the three select boxes, it doesn't work? Why's that? – Jose Mar 3 '17 at 16:12
  • @Jose Just updated the answer to address the possibility of supplier/product/delivery params being blank. – Brandon Kelly Mar 3 '17 at 17:31

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.