Answers.Body: <p>The issue with your current code is that you're looping over each type/size category, creating a parameter in your <code>relatedTo</code> query for <em>each one</em>. This means that you're currently creating a query that looks like this:</p>
  
  <pre><code>relatedTo([
      'and',
      { targetElement: 1 },
      { targetElement: 2 },
      { targetElement: 3 },
      { targetElement: 4 }
  ])
  </code></pre>
  
  <p>...which, combined with the <code>AND</code>, means that Craft will only return entries that have all of these relations.</p>
  
  <p>What you need to do, is to <em>collect the IDs for each category group in an array, and add those arrays to the <code>relatedTo</code> query</em> – i.e. create a query that looks like this:</p>
  
  <pre><code>relatedTo([
      'and',
      { targetElement: [1, 2] },
      { targetElement: [3, 4] }
  ])
  </code></pre>
  
  <p>The above query would solve your issue, because whenever Craft isn't given an <code>and</code> or <code>or</code>, it assumes <code>or</code>. This means that the above query is exactly the same as the below:  </p>
  
  <pre><code>relatedTo([
      'and',
      { targetElement: ['or', 1, 2] },
      { targetElement: ['or', 3, 4] }
  ])
  </code></pre>
  
  <p>Essentially, the above query is saying: give me all entries that have a relation matching either the target element 1 OR 2, AND a relation matching either the target element 3 or 4.</p>
  
  <p>Here's how the Twig to create a query like the above could look:</p>
  
  <pre><code>{# Create the base query #}
  {% set query = craft.entries({ section: 'products' }) %}
  
  {% set relatedTo = [] %}
  
  {% set types = craft.app.request.getQueryParam('type')|split(',')|filter %}
  {% if types %}
      {% set relatedTo = relatedTo|merge([{
          targetElement: craft.categories({ group: 'types', slug: types }).ids()
      }]) %}
  {% endif %}
  
  {% set sizes = craft.app.request.getQueryParam('size')|split(',')|filter %}
  {% if sizes %}
      {% set relatedTo = relatedTo|merge([{
          targetElement: craft.categories({ group: 'sizes', slug: sizes }).ids()
      }]) %}
  {% endif %}
  
  {% if relatedTo|length &gt; 1 %}
      {% set relatedTo = ['and']|merge(relatedTo) %}
  {% endif %}
  
  {% set entries = query.relatedTo(relatedTo ? relatedTo : null).all() %}
  </code></pre>
  
  <p>The above code should work for all the below use cases:  </p>
  
  <ul>
  <li>/products (returns all product entries)</li>
  <li>/products?type=polo-shirt (returns all polo shirts)</li>
  <li>/products?type=polo-shirt&amp;size=small (returns all small polo shirts)</li>
  <li>/products?type=polo-shirt,jeans&amp;size=small (returns all small polo shirts and jeans)</li>
  <li>/products?type=polo-shirt,jeans&amp;size=small,medium (returns all small and medium polo shirts and jeans)</li>
  </ul>
  
  <p>Note that the usage of <code>ids()</code> over <code>all()</code> in a few places (such as in the <code>craft.categories</code> queries to get the type and size categories) – this is a performance tweak (it's more effective to just pull the IDs than the entire model).</p>
  
  <p>Also note that if you want to use pagination, just amend the final line to</p>
  
  <pre><code>{% paginate query.relatedTo(relatedTo ? relatedTo : null).limit(10) as pageInfo, pageEntries %}
  </code></pre>
  
  <p><em><strong>Note:</strong> I originally typed out a completely different answer to this question, but after some helpful discussion with @Robin Schambach on Slack, we arrived on the above, IMO much better solution. Cheers Robin!</em></p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-07-22T13:03:36.037Z
Answers.LastActivityDate: 2018-07-22T14:25:46.257Z
Answers.LastEditDate: 2018-07-22T14:25:46.257Z
Answers.UserDisplayName: Mats Mikkel Rummelhoff
Body: <p>I am trying to filter using categories. I have to filters type and size, in the url.</p>
  
  <pre><code>/products.html?size=medium,large&amp;type=polo-shirt,jeans
  </code></pre>
  
  <p>In my template, I am doing the following</p>
  
  <pre><code>{% set sizes = craft.app.request.getQueryParam('size') %}
  {% set types = craft.app.request.getQueryParam('type') %}
  
  {% set relationParam = ['and'] %}
  
  {# type #}
  {% if types %}
    {% for i in types|split(",") %}
      {% set type = craft.categories.group('types').slug(i) %}
      {% set relationParam = relationParam|merge([{ targetElement: type }]) %}
    {% endfor %}
  {% endif %}
  
  {# sizes #}
  {% if sizes %}
    {% for i in sizes|split(",") %}
      {% set size = craft.categories.group('sizes').slug(i) %}
      {% set relationParam = relationParam|merge([{ targetElement: size }]) %}
    {% endfor %}
  {% endif %}
  
  {# Avoid passing an array with `['and']` only #}
  {% if relationParam|length &lt; 2 %}
    {% set relationParam = '' %}
  {% endif %}
  
  {% set entries = craft.entries({
      section: 'products',
      relatedTo:  relationParam
  }) %}
  
  &lt;div class="listings-container"&gt;
    {% if entries|length %}
      {% for entry in entries %}
          {% include "partials/_listing.html" %}
      {% endfor %}
    {% endif %}
  &lt;/div&gt;
  </code></pre>
  
  <p>The problem I'm having is as follows:</p>
  
  <ol>
  <li>I click on “medium” in sizes, it shows all products that have the category “medium”</li>
  <li>I then, click on “large” in sizes, it shows one result because it’s searching for products that have all 2 categories (“medium”, “large”)</li>
  <li>I then click on "polo-shirt" in types, this now shows all products that have all 3 categories (“medium”, “large", "polo-shirt")</li>
  <li>I then click on “jeans” in sizes, this now shows no results as its trying to find products that have all 4 categories (“medium”, “large", "polo-shirt", "jeans")</li>
  </ol>
  
  <p>What I am trying to achieve is find all the products that are “medium” or “large" sizes and of "polo-shirt" or “jeans” types</p>
  
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2018-07-20T16:33:39.133Z
Comments.Text: Would it be an option to do it in PHP? Because it's a mess in Twig
Comments.UserDisplayName: Robin Schambach
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2018-07-20T16:38:50.200Z
Comments.Text: @RobinSchambach nope, it needs to be Twig I'm afraid
Comments.UserDisplayName: Hassan Mahmood
Tags: <categories><relations><filter><relatedto><querystring>
Title: Filtering multiple categories with URL queries
ThreadText: # Filtering multiple categories with URL queries
  
  (Asked by: username_0 on 2018-07-20)
  
  I am trying to filter using categories. I have to filters type and size, in the url.
  
  ```
  /products.html?size=medium,large&type=polo-shirt,jeans
  
  ```
  
  In my template, I am doing the following
  
  ```
  {% set sizes = craft.app.request.getQueryParam('size') %}
  {% set types = craft.app.request.getQueryParam('type') %}
  
  {% set relationParam = ['and'] %}
  
  {# type #}
  {% if types %}
    {% for i in types|split(",") %}
      {% set type = craft.categories.group('types').slug(i) %}
      {% set relationParam = relationParam|merge([{ targetElement: type }]) %}
    {% endfor %}
  {% endif %}
  
  {# sizes #}
  {% if sizes %}
    {% for i in sizes|split(",") %}
      {% set size = craft.categories.group('sizes').slug(i) %}
      {% set relationParam = relationParam|merge([{ targetElement: size }]) %}
    {% endfor %}
  {% endif %}
  
  {# Avoid passing an array with `['and']` only #}
  {% if relationParam|length < 2 %}
    {% set relationParam = '' %}
  {% endif %}
  
  {% set entries = craft.entries({
      section: 'products',
      relatedTo:  relationParam
  }) %}
  
  <div class="listings-container">
    {% if entries|length %}
      {% for entry in entries %}
          {% include "partials/_listing.html" %}
      {% endfor %}
    {% endif %}
  </div>
  
  ```
  
  The problem I'm having is as follows:
  
  1. I click on “medium” in sizes, it shows all products that have the category “medium”
  2. I then, click on “large” in sizes, it shows one result because it’s searching for products that have all 2 categories (“medium”, “large”)
  3. I then click on "polo-shirt" in types, this now shows all products that have all 3 categories (“medium”, “large", "polo-shirt")
  4. I then click on “jeans” in sizes, this now shows no results as its trying to find products that have all 4 categories (“medium”, “large", "polo-shirt", "jeans")
  
  What I am trying to achieve is find all the products that are “medium” or “large" sizes and of "polo-shirt" or “jeans” types
  
  **Tags:** categories, relations, filter, relatedto, querystring
  
  > **Comments:**
  > **username_1**: Would it be an option to do it in PHP? Because it's a mess in Twig  
  > **username_0**: @username_1 nope, it needs to be Twig I'm afraid  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_2 on 2018-07-22. Score: 7 | Accepted answer
  
  The issue with your current code is that you're looping over each type/size category, creating a parameter in your `relatedTo` query for *each one*. This means that you're currently creating a query that looks like this:
  
  ```
  relatedTo([
      'and',
      { targetElement: 1 },
      { targetElement: 2 },
      { targetElement: 3 },
      { targetElement: 4 }
  ])
  
  ```
  
  ...which, combined with the `AND`, means that Craft will only return entries that have all of these relations.
  
  What you need to do, is to *collect the IDs for each category group in an array, and add those arrays to the `relatedTo` query* – i.e. create a query that looks like this:
  
  ```
  relatedTo([
      'and',
      { targetElement: [1, 2] },
      { targetElement: [3, 4] }
  ])
  
  ```
  
  The above query would solve your issue, because whenever Craft isn't given an `and` or `or`, it assumes `or`. This means that the above query is exactly the same as the below:
  
  ```
  relatedTo([
      'and',
      { targetElement: ['or', 1, 2] },
      { targetElement: ['or', 3, 4] }
  ])
  
  ```
  
  Essentially, the above query is saying: give me all entries that have a relation matching either the target element 1 OR 2, AND a relation matching either the target element 3 or 4.
  
  Here's how the Twig to create a query like the above could look:
  
  ```
  {# Create the base query #}
  {% set query = craft.entries({ section: 'products' }) %}
  
  {% set relatedTo = [] %}
  
  {% set types = craft.app.request.getQueryParam('type')|split(',')|filter %}
  {% if types %}
      {% set relatedTo = relatedTo|merge([{
          targetElement: craft.categories({ group: 'types', slug: types }).ids()
      }]) %}
  {% endif %}
  
  {% set sizes = craft.app.request.getQueryParam('size')|split(',')|filter %}
  {% if sizes %}
      {% set relatedTo = relatedTo|merge([{
          targetElement: craft.categories({ group: 'sizes', slug: sizes }).ids()
      }]) %}
  {% endif %}
  
  {% if relatedTo|length > 1 %}
      {% set relatedTo = ['and']|merge(relatedTo) %}
  {% endif %}
  
  {% set entries = query.relatedTo(relatedTo ? relatedTo : null).all() %}
  
  ```
  
  The above code should work for all the below use cases:
  
  * /products (returns all product entries)
  * /products?type=polo-shirt (returns all polo shirts)
  * /products?type=polo-shirt&size=small (returns all small polo shirts)
  * /products?type=polo-shirt,jeans&size=small (returns all small polo shirts and jeans)
  * /products?type=polo-shirt,jeans&size=small,medium (returns all small and medium polo shirts and jeans)
  
  Note that the usage of `ids()` over `all()` in a few places (such as in the `craft.categories` queries to get the type and size categories) – this is a performance tweak (it's more effective to just pull the IDs than the entire model).
  
  Also note that if you want to use pagination, just amend the final line to
  
  ```
  {% paginate query.relatedTo(relatedTo ? relatedTo : null).limit(10) as pageInfo, pageEntries %}
  
  ```
  
  ***Note:** I originally typed out a completely different answer to this question, but after some helpful discussion with @username_1 on Slack, we arrived on the above, IMO much better solution. Cheers username_1!*
  
  
  ---
  
  