I am building a shop with book titles, but most for book titles there is not only a book available for purchase, but there could also be a poster of the book, a bookmark and possibly other products, all related to the book title.

I have these three sections:

  1. A channeltitles’ (where meta content regarding each book is kept)
  2. A channelproducts’ 
- has a relation field to the book title entry 
- has a relation field to the productCategories entry
  3. A structureproductCategories’ (with entries ‘book’, ‘poster’, ‘bookmark’ etc)

In short: I need to get the productCategories that are related to each book title. If this were a direct relation, this would more or less do:

{% set relatedProductCategories = craft.entries()
      .relatedTo(entry) %}

However, the relation between these two goes through the product entry: when creating a new product entry, you select the relations to the book title, and the product category.

On the book titles index/listing page, the book title entries are displayed as cards, with a cover image and the title of the book. On these cards however, I also want to display what type of products (productCategories) there are for this book title as little labels. So users can see if there is just the book available, or also other products related to this book. A bit like this:

WAR AND PEACE [book] [bookmark] [poster]

THE LORD OF THE RINGS [book] [poster] [calendar]

I can access the product categories with an element query looking for entries in ‘products’ that are (reverse) related to the book title, and then reference the productCategory relation field:

{% set cards = craft.entries()
       .all() %}

{% for entry in cards %}

     <h3>{{ entry.title }}</h3>

     {% set relatedProducts = craft.entries
            .relatedTo(entry) %}

      {% for product in relatedProducts %}
           {{ product.productCategory[0] }}
      {% endfor %}

{% endfor %}

This works in displaying the productCategories, but when I have multiple products of the same productCategory, they are printed as many times. So on the card, if I would have two posters, it will say:

WAR AND PEACE [book] [bookmark] [poster] [poster]

How do I avoid these duplicates?

Also, the results are displayed in order of product entries, not in order of the productCategories, so it’s currently actually more like this:

WAR AND PEACE [poster] [bookmark] [book] [poster]

How would I order these results in accordance with the structure of the section productCategories?

1 Answer 1


Use two queries: The first to get all products related to the current title, the second one to get all product categories related to any of the products. The query can't return duplicates, so multiple products from the same category won't result in duplicates. It also allows you to sort the categories returned by the query however you want.

{% for entry in cards %}
    {% set relatedProducts = craft.entries()
    {% set categoriesForAllRelatedProducts = craft.entries()

    <h3>{{ entry.title }}</h3>
    {{ categoriesForAllRelatedProducts|map(c => "[#{c.title}]")|join(' ') }}
{% endfor %}
  • 1
    Thank you SO much, that works perfectly! Took me a little while to wrap my brain around the 'chaining' of the relations, but actually a very clear and clean solution. Even introduced me to a concrete example of a Twig map filter. Cheers!
    – Jolle
    Commented Oct 21, 2022 at 17:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.