How to filter entries in multiple category groups by selecting multiple categories This question is very similar to the question posted here I have asked in the comments but my requirements are slightly different. I am trying to filter search results by categories in more than one category group and use both an exclusive search (category AND category) and a non exclusive search (category OR category).

My use case is this: the parks category wants to filter using the OR filter so Park A OR Park B and so does the price filter so Price A OR Price B but when they are combined Park A AND Price B only show parks in the Price A category.
After a lot of back and forth and reading various posts on this I thought I would submit my answer. There maybe better ways of doing what I ended up doing but hopefully this will provide a start for someone wanting to do the same.

First here are a few posts and questions that helped:


Find entries with multiple categories with an and/or twist
How can I create an ElementCriteriaModel with &#39;and&#39; and &#39;or&#39;
Combined searches and filters using Craft


My Solution

First I created a search form that had the checkboxes for the search, each checkbox name when posted would form the get variable for the filter. A check is done to see if there are categories, if there are then I loop through and add them. There is also a check done to see if you are on the filter results page, if you are then set the relevant checkboxes to checked on the form.

The Search Form Code

&lt;form action="{{ url('homes-for-sale/filter') }}" method="get"&gt;

  {# check if there are any park categories if there are then show them as checkboxes #}

  {% if parkCategories|length %}

    {% for category in parkCategories %}
      {% set loopIndex = 'park' ~ loop.index %}
      &lt;label class="checkbox-inline"&gt;

        {# if you are on the filter results page then set the results to checked #}

        {% if template is defined and template == 'filter' %}
          &lt;input type="checkbox" name="{{ loopIndex }}" value="{{ category.slug }}"{% if category.slug == park1 or category.slug == park2 or category.slug == park3 or category.slug == park4 or category.slug == park5 %} checked{% endif %}&gt;
        {% else %}
          &lt;input type="checkbox" name="{{ loopIndex }}" value="{{ category.slug }}"&gt;
        {% endif %}
        {{ category.navTitle }}
      &lt;/label&gt;
    {% endfor %}
  {% endif %}

  {# check if there are any price categories if there are then show them as checkboxes #}

  {% if priceCategories|length %}
    {% for category in priceCategories %}
      {% set loopIndex = 'price' ~ loop.index %}
      &lt;label class="checkbox-inline"&gt;

        {# if you are on the filter results page then set the results to checked #}

        {% if template is defined and template == 'filter' %}
          &lt;input type="checkbox" name="{{ loopIndex }}" value="{{ category.slug }}"{% if category.slug == price1 or category.slug == price2 or category.slug == price3 or category.slug == price4 or category.slug == price5 or category.slug == price6 %} checked{% endif %}&gt;
        {% else %}
          &lt;input type="checkbox" name="{{ loopIndex }}" value="{{ category.slug }}"&gt;
        {% endif %}
        {{ category.title }}
      &lt;/label&gt;
    {% endfor %}
  {% endif %}

  &lt;button type="submit"&gt;Submit&lt;/button&gt;

&lt;/form&gt;


The Filter Results Page

On the results page you need to check which filter variables have been set via the get request and then display the appropriate related entries.

{# set park search parameters #}
{% set park1 = craft.request.getParam('park1') %}
{% set park2 = craft.request.getParam('park2') %}
{% set park3 = craft.request.getParam('park3') %}
{% set park4 = craft.request.getParam('park4') %}
{% set park5 = craft.request.getParam('park5') %}

{# set price search parameters #}
{% set price1 = craft.request.getParam('price1') %}
{% set price2 = craft.request.getParam('price2') %}
{% set price3 = craft.request.getParam('price3') %}
{% set price4 = craft.request.getParam('price4') %}
{% set price5 = craft.request.getParam('price5') %}
{% set price6 = craft.request.getParam('price6') %}

{# set park category model if it is in the search query #}
{% set parkCategory1 = park1|length ? craft.categories.slug(park1).first().id %}
{% set parkCategory2 = park2|length ? craft.categories.slug(park2).first().id %}
{% set parkCategory3 = park3|length ? craft.categories.slug(park3).first().id %}
{% set parkCategory4 = park4|length ? craft.categories.slug(park4).first().id %}
{% set parkCategory5 = park5|length ? craft.categories.slug(park5).first().id %}

{# create an array of park categories #}
{% set parks = [
  parkCategory1,
  parkCategory2,
  parkCategory3,
  parkCategory4,
  parkCategory5,
] %}

{# create a park array and trim blank strings #}
{% set trimParks = [] %}

{% for park in parks %}
  {% if park|length %}
    {% set trimParks = trimParks|merge({ (loop.index0): park}) %}
  {% endif %}
{% endfor %}

{# set price category model if it is in the search query #}
{% set priceCategory1 = price1|length ? craft.categories.slug(price1).first().id %}
{% set priceCategory2 = price2|length ? craft.categories.slug(price2).first().id %}
{% set priceCategory3 = price3|length ? craft.categories.slug(price3).first().id %}
{% set priceCategory4 = price4|length ? craft.categories.slug(price4).first().id %}
{% set priceCategory5 = price5|length ? craft.categories.slug(price5).first().id %}
{% set priceCategory6 = price6|length ? craft.categories.slug(price6).first().id %}

{# create an array of price categories #}
{% set prices = [
  priceCategory1,
  priceCategory2,
  priceCategory3,
  priceCategory4,
  priceCategory5,
  priceCategory6,
] %}

{# create a price array and trim blank strings #}
{% set trimPrices = [] %}

{% for price in prices %}
  {% if price|length %}
    {% set trimPrices = trimPrices|merge({ (loop.index0): price}) %}
  {% endif %}
{% endfor %}

{% if trimParks|length and trimPrices|length %}
  {% set entries = craft.entries.section('homesForSale').relatedTo('and',
    {targetElement:parks},
    {targetElement:prices}
  ) %}
{% endif %}

{% if trimParks|length and not trimPrices|length %}
  {% set entries = craft.entries.section('homesForSale').relatedTo('or',
    {targetElement:parks}
  ) %}
{% endif %}

{% if trimPrices|length and not trimParks|length %}
  {% set entries = craft.entries.section('homesForSale').relatedTo('or',
    {targetElement:prices}
  ) %}
{% endif %}

&lt;h1&gt;Filter Results&lt;/h1&gt;

{% if entries|length %}
    &lt;p&gt;{{ entries|length }} results:&lt;/p&gt;
    &lt;ul&gt;
        {% for entry in entries %}
          {% set park = entry.park.first() %}
          {% set price = entry.parkPriceCategory.first() %}
          &lt;li&gt;&lt;a href="{{ entry.url }}"&gt;{{ entry.title }} :: &lt;b&gt;{{ park }}&lt;/b&gt; :: &lt;b&gt;{{ price }}&lt;/b&gt;&lt;/a&gt;&lt;/li&gt;
        {% endfor %}
    &lt;/ul&gt;
{% else %}
    &lt;p&gt;Your search didn't return any results.&lt;/p&gt;
{% endif %}