<p>Ideally, you'll use a dynamic route that can match any number of tags – negating the need to hard code the number of possible tag segments.</p>

<p>You can't pull this off using the Routes admin inside the CP, so crack open your <strong>/craft/config/routes.php</strong> file and add this:</p>

<pre><code>return array(
    'tags/(.+)' =&gt; 'path_to_your_tags_template',
); 
</code></pre>

<p>The above route can have any number of segments under <code>/tags</code>, but will require at least one. <em>Be sure to delete any conflicting routes defined in Settings -> Routes!</em></p>

<p>Inside your tags template, you can get an array of all the tags in the URI like this (the <code>|slice(1)</code> part removes the first segment, i.e. "tags"):</p>

<pre><code>{% set tagSegments = craft.request.segments|slice(1) %}
</code></pre>

<p>To pull the actual tags, you can do something like this:</p>

<pre><code>{% set tags = craft.tags.slug(tagSegments) %}
</code></pre>

<p>Pulling it all together, here's how a (simplified) version of your template could look:</p>

<pre><code>{# Get tags #}
{% set tagSegments = craft.request.segments|slice(1) %}
{% set tags = craft.tags.slug(tagSegments) %}

{# Get entries related to tags #}
{% set relatedTo = [] %}
{% for tagId in tags.ids() %}
    {% set relatedTo = relatedTo|merge([{
        targetElement: tagId,
        field: 'tags'
    }]) %}
{% endfor %}
{% if relatedTo|length &gt; 1 %}
    {% set relatedTo = ['and']|merge(relatedTo) %}
{% endif %}
{% set entries = craft.entries.relatedTo(relatedTo) %}

{% if entries|length %}
    &lt;h1&gt;Entries tagged with {% for tag in tags %}{{ tag.title }}{% if not loop.last %}{{ loop.revindex == 2 ? ' and' : ',' }}{% endif %}{% endfor %}&lt;/h1&gt;
    &lt;ul&gt;
        {% for entry in entries %}
            &lt;li&gt;{{ entry.getLink() }}..&lt;/li&gt;
        {% endfor %}
    &lt;/ul&gt;
{% else %}
    &lt;p&gt;No entries could be found with {{ tagSegments|length &gt; 1 ? 'those tags' : 'that tag' }}.&lt;/p&gt;
{% endif %}
</code></pre>

CC BY-SA 3.0
2016-12-05T00:45:30.480Z
Thanks for your help! This (kinda) works. The problem is that it returns all posts that contain one of these tags. It should return posts that match all  the tags. Its returning as if the tags are being OR'd and not ANDed which seems strange since 'and' is clearly being defined inside the relatedTo() function.
Markus
CC BY-SA 3.0
2016-12-05T06:41:39.850Z
@Markus Yeah, that's a little strange. I've updated my answer with an alternative, more convoluted but possibly more robust solution. Another thing you could try is simply `craft.entries.relatedTo('and', tags.ids())` (i.e. without joining the array of IDs). Sorry, don't have time to test either of these solutions right now.
Mats Mikkel Rummelhoff
CC BY-SA 3.0
2016-12-06T01:44:35.720Z
Thank you! Your updated solution seems to work. I appreciate the help.
Markus
CC BY-SA 3.0
2016-12-04T09:44:40.817Z
2016-12-05T06:40:09.603Z
2016-12-05T06:40:09.603Z
Mats Mikkel Rummelhoff
<p>how would I go about setting up a route and twig template to filter by multiple tags?</p>

<pre><code>Example URL scheme: example.com/tags/tag1/tag2/tag3
</code></pre>

<p>where <code>tag1</code>, <code>tag2</code> and <code>tag3</code> are tags defined in a tag field inside an entry. The resulting entries list should only output entries with <code>tag1</code> <strong>AND</strong> <code>tag2</code> <strong>AND</strong> <code>tag3</code>. <code>tag2</code> and <code>tag3</code> should be optional. I want to avoid the use of get params such as ?tag=tag1,tag2,tag3.</p>

<p>The ultimate goal is chain multiple tags to get finer results, similar to pinboard.in tag chaining. </p>

<p>Example:<a href="https://pinboard.in/t:javascript/t:css/t:webdev" rel="nofollow noreferrer">https://pinboard.in/t:javascript/t:css/t:webdev</a>
will return entries with <code>javascript</code> AND <code>css</code> AND <code>webdev</code></p>

<hr>

<p>My route looks as follows</p>

<pre><code>URI: tags/tag-token/tag-taken2/tag-token3
</code></pre>

<p>and my twig template looks like</p>

<pre><code>{% set entries = craft.entries.relatedTo(tag) %}

{% if craft.request.getSegment(2) !='' %}
&lt;h1&gt;Entries tagged with “{{ tag.title }}”&lt;/h1&gt;
{% if entries | length %}
    &lt;ul&gt;
        {% for entry in entries %}
            &lt;li&gt;{{ entry.getLink() }}..&lt;/li&gt;
        {% endfor %}
    &lt;/ul&gt;
{% else %}
    &lt;p&gt;No entries could be found with that tag.&lt;/p&gt;
{% endif %}
{% endif %}


{% if craft.request.getSegment(3) !='' %}
{% set tag2entries = craft.entries.relatedTo('and',
    { element: entry, field: 'tags' },
    { element: entries, field: 'tags' }
) %}
{% set tag2entries = craft.entries.relatedTo(tag2-token) %}
&lt;h1&gt;Entries tagged with “{{ tag.title }} and {{ tag-token2}}”&lt;/h1&gt;
{% if tag2entries | length %}
    &lt;ul&gt;
        {% for entry in tag2entries %}
            &lt;li&gt;{{ entry.getLink() }}..&lt;/li&gt;
        {% endfor %}
    &lt;/ul&gt;
{% else %}
    &lt;p&gt;No entries could be found with those tags.&lt;/p&gt;
{% endif %}
{% endif %}

{% if craft.request.getSegment(4) !='' %}
{% set tag3entries = craft.entries.relatedTo(tag3-token) %}
&lt;h1&gt;Entries tagged with “{{ tag.title }} and {{ tag-token2}} and {{tag-token3}}”&lt;/h1&gt;
{% if tag3entries | length %}
    &lt;ul&gt;
        {% for entry in tag3entries %}
            &lt;li&gt;{{ entry.getLink() }}..&lt;/li&gt;
        {% endfor %}
    &lt;/ul&gt;
{% else %}
    &lt;p&gt;No entries could be found with those tags.&lt;/p&gt;
{% endif %}
{% endif %}
</code></pre>

<p>I'm am not sure how to go about using the relatedTo() function to filter entries based on those 3 tag parameters in the URL.</p>

<p>Any insight on how to go about this?</p>

<templating><tags><filtering>
Display entries that meet multiple tag params
# Display entries that meet multiple tag params

(Asked by: username_0 on 2016-12-03)

how would I go about setting up a route and twig template to filter by multiple tags?
Example URL scheme: example.com/tags/tag1/tag2/tag3

where tag1, tag2 and tag3 are tags defined in a tag field inside an entry. The resulting entries list should only output entries with tag1 AND tag2 AND tag3. tag2 and tag3 should be optional. I want to avoid the use of get params such as ?tag=tag1,tag2,tag3.
The ultimate goal is chain multiple tags to get finer results, similar to pinboard.in tag chaining. 
Example:https://pinboard.in/t:javascript/t:css/t:webdev
will return entries with javascript AND css AND webdev

My route looks as follows
URI: tags/tag-token/tag-taken2/tag-token3

and my twig template looks like
{% set entries = craft.entries.relatedTo(tag) %}

{% if craft.request.getSegment(2) !='' %}
Entries tagged with “{{ tag.title }}”
{% if entries | length %}
    
        {% for entry in entries %}
            {{ entry.getLink() }}..
        {% endfor %}
    
{% else %}
    No entries could be found with that tag.
{% endif %}
{% endif %}


{% if craft.request.getSegment(3) !='' %}
{% set tag2entries = craft.entries.relatedTo('and',
    { element: entry, field: 'tags' },
    { element: entries, field: 'tags' }
) %}
{% set tag2entries = craft.entries.relatedTo(tag2-token) %}
Entries tagged with “{{ tag.title }} and {{ tag-token2}}”
{% if tag2entries | length %}
    
        {% for entry in tag2entries %}
            {{ entry.getLink() }}..
        {% endfor %}
    
{% else %}
    No entries could be found with those tags.
{% endif %}
{% endif %}

{% if craft.request.getSegment(4) !='' %}
{% set tag3entries = craft.entries.relatedTo(tag3-token) %}
Entries tagged with “{{ tag.title }} and {{ tag-token2}} and {{tag-token3}}”
{% if tag3entries | length %}
    
        {% for entry in tag3entries %}
            {{ entry.getLink() }}..
        {% endfor %}
    
{% else %}
    No entries could be found with those tags.
{% endif %}
{% endif %}

I'm am not sure how to go about using the relatedTo() function to filter entries based on those 3 tag parameters in the URL.
Any insight on how to go about this?

**Tags:** templating, tags, filtering


---

## 1 answers

### Answer by username_1 on 2016-12-04. Score: 1 | Accepted answer

Ideally, you'll use a dynamic route that can match any number of tags – negating the need to hard code the number of possible tag segments.
You can't pull this off using the Routes admin inside the CP, so crack open your /craft/config/routes.php file and add this:
return array(
    'tags/(.+)' => 'path_to_your_tags_template',
); 

The above route can have any number of segments under /tags, but will require at least one. Be sure to delete any conflicting routes defined in Settings -> Routes!
Inside your tags template, you can get an array of all the tags in the URI like this (the |slice(1) part removes the first segment, i.e. "tags"):
{% set tagSegments = craft.request.segments|slice(1) %}

To pull the actual tags, you can do something like this:
{% set tags = craft.tags.slug(tagSegments) %}

Pulling it all together, here's how a (simplified) version of your template could look:
{# Get tags #}
{% set tagSegments = craft.request.segments|slice(1) %}
{% set tags = craft.tags.slug(tagSegments) %}

{# Get entries related to tags #}
{% set relatedTo = [] %}
{% for tagId in tags.ids() %}
    {% set relatedTo = relatedTo|merge([{
        targetElement: tagId,
        field: 'tags'
    }]) %}
{% endfor %}
{% if relatedTo|length > 1 %}
    {% set relatedTo = ['and']|merge(relatedTo) %}
{% endif %}
{% set entries = craft.entries.relatedTo(relatedTo) %}

{% if entries|length %}
    Entries tagged with {% for tag in tags %}{{ tag.title }}{% if not loop.last %}{{ loop.revindex == 2 ? ' and' : ',' }}{% endif %}{% endfor %}
    
        {% for entry in entries %}
            {{ entry.getLink() }}..
        {% endfor %}
    
{% else %}
    No entries could be found with {{ tagSegments|length > 1 ? 'those tags' : 'that tag' }}.
{% endif %}

> **Comments:**
> **username_0**: Thanks for your help! This (kinda) works. The problem is that it returns all posts that contain one of these tags. It should return posts that match all  the tags. Its returning as if the tags are being OR'd and not ANDed which seems strange since 'and' is clearly being defined inside the relatedTo() function.  
> **username_1**: @username_0 Yeah, that's a little strange. I've updated my answer with an alternative, more convoluted but possibly more robust solution. Another thing you could try is simply `craft.entries.relatedTo('and', tags.ids())` (i.e. without joining the array of IDs). Sorry, don't have time to test either of these solutions right now.  
> **username_0**: Thank you! Your updated solution seems to work. I appreciate the help.  

---

