How to display entries filtered from more than one category I want to try to learn how I can filter entries by multiple categories. I realise similar questions have been asked in the past, but all the answers are for specific use cases. All I want to do is filter entries by categories from more than one group. I've found code to set up a form that returns the URL with a string, but I understand that the string needs to be converted into an array. That's the part I can't work out how to do.
So the form is like this:

        {# Get the relevant entries #}
    {% set categoryQuery = craft.app.request.getParam('category') %}
    {% set categories = craft.categories.group('candidate').slug(categoryQuery) %}

    {# Set the search parameters #} 
    {% set searchParams = {
        relatedTo: categories,
        section: 'candidate'
    } %}

    {# Get the relevant entries #}
    {% set relevantEntries = craft.entries(searchParams) %}

    &lt;form action="{{ craft.app.request.absoluteUrl }}"&gt;

        &lt;p&gt;Eye colour&lt;/p&gt;

        {% for category in craft.categories.group('eyeColour').all() %}
            &lt;input id="{{category.slug}}" type="checkbox" name="category[]"  value="{{ category.slug }}" {{ category.slug in categoryQuery or categoryQuery == null  ? 'checked'}}&gt;
            &lt;label for="{{ category.slug }}" &gt;{{ category.title }}&lt;/label&gt;
        {% endfor %}

        &lt;p&gt;Hair colour&lt;/p&gt;

        {% for category in craft.categories.group('hairColour').all() %}
            &lt;input id="{{category.slug}}" type="checkbox" name="category[]"  value="{{ category.slug }}" {{ category.slug in categoryQuery or categoryQuery == null  ? 'checked'}}&gt;
            &lt;label for="{{ category.slug }}" &gt;{{ category.title }}&lt;/label&gt;
        {% endfor %}

        &lt;p&gt;&lt;button type="submit"&gt;
                Search
        &lt;/button&gt;&lt;/p&gt;               

    &lt;/form&gt;


Which gives me a URL like this

/?category%5B%5D=blue&amp;category%5B%5D=black
Here's something I've got for filtering news by author and by category. You might be able to re-work it for your needs?

From a page URL being: http://project.com/news?&amp;cat=CATEGORY&amp;author=NAME

{% set query = {} %}

{% set categoryParam = craft.app.request.getParam('cat') %}
{% set authorParam = craft.app.request.getParam('author') %}

{% set category = craft.categories.slug(categoryParam) %}
{% set author = craft.entries.section('team').slug(authorParam).first() %}

{% if category %}
    {% set query = query|merge(category) %}
{% endif %}

{% if author %}
    {% set query = query|merge(author) %}
{% endif %}

{% if categoryParam and authorParam %}

    {% set results = targetEntries.relatedTo(['and',
            { targetElement: category },
            { targetElement: author }
        ]
    ).orderBy('postDate desc') %}

    {% set noResultsMessage = "Sorry, there are no articles by " ~ author.title ~ " in that category. Try changing your filters." %}

{% elseif categoryParam %}

    {% set results = targetEntries.relatedTo(category).orderBy('postDate desc') %}
    {% set noResultsMessage = "Sorry, there are no articles in that category. Try filtering by a different category." %}

{% elseif authorParam %}

    {% set results = targetEntries.relatedTo(author).orderBy('postDate desc') %}
    {% set noResultsMessage = "Sorry, there are no articles by " ~ author.title ~ ". Try filtering by someone else." %}

{% else %}

    {% set results = targetEntries.orderBy('postDate desc') %}

{% endif %}


It was written a long time ago, so I'm seeing some places where I can streamline it, but this works for me filtering by two fields. I think the key part for your requirements is:

{% set results = targetEntries.relatedTo(['and',
    { targetElement: category },
    { targetElement: author }
]).orderBy('postDate desc') %}


Hope this helps in some way.