2

I'm struggling with how to do this in Twig... I feel like it would be easier in a full programming language.

If an entry's categories look like this:

A
    - A1
    - A2
B
C
    - C1

I need to output a list like this:

A > A1
A > A2
B
C > C1

I can't seem to figure out how to avoid printing all the top-level categories on their own. This is what I've managed to output:

A
A > A1
A > A2
B
C
C > C1

I have a feeling I'm going to feel dumb when someone answers this, but any help is appreciated.


Edit: I've found a solution.

Before listing the categories, I run a for loop on just the entry's level 2 categories and collect their parents' IDs using an empty array and the merge filter. Then, when listing the categories, I put a conditional so that level 1 categories are not listed if their ID is in the array.

It seems an awkward way to do it, and I'm still interested in better solutions.

2 Answers 2

2

Collecting all of the categories' ancestors is probably the way to go in that case. So the solution you already posted within your question is more or less the same as I was about to post.

Instead of having a conditional in the output loop, you could also prepare an array of your bottom level category models using Craft's without filter (I always try to separate output from query / logic blocks in my templates).

This works with any number of category levels.

{% set allCategories = craft.categories.relatedTo(entry) %}

{% set ancestorCatIds = [] %}
{% for category in allCategories %}
    {% set ancestorCatIds = ancestorCatIds|merge(category.ancestors.ids()) %}
{% endfor %}

{% set bottomCatIds = allCategories.ids()|without(ancestorCatIds) %}
{% set bottomCategories = craft.categories.id(bottomCatIds) %}

<ul>
    {% for category in bottomCategories %}
        <li>{{ macros.catBreadcrumbs(category) }}</li>
    {% endfor %}
</ul>

I made a macro that outputs the category breadcrumbs, to have even less logic in the output block.

{% macro catBreadcrumbs(category) %}
    {% set ancestorCategories = category.ancestors.find() %}
    {% set ancestorCategories = ancestorCategories|merge([category]) %}
    {{ ancestorCategories|join(' > ') }}
{% endmacro %}
1

I also had to make an array of categories that have children related to the entry. Then hide those categories when running the output loop. This is as concise as I could make the code. Assuming your category field name is myCats, this will do the trick:

{% set hiddenCats = [] %}
{% for category in entry.myCats.level(2) %}
  {% set hiddenCats = hiddenCats|merge([category.parent.id]) %}
{% endfor %}

<ul>
  {% for category in entry.myCats if category.id not in hiddenCats %}
    <li>
      {% if category.parent %}
        {{ category.parent.title }} &gt;
      {% endif %}

      {{ category.title }}
    </li>
  {% endfor %}
</ul>
2
  • What if category "B" does actually have child categories, but they're just not related to the current entry.
    – carlcs
    Commented Apr 20, 2015 at 8:23
  • Oops. I didn't see that we were dealing with a list of categories related to an entry. I'll revise my answer.
    – Alex Roper
    Commented Apr 20, 2015 at 11:00

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.