1

Been trying to get this working for a few hours but no joy, hope someone can help.

I'm looking like an post archive of all entries.

Something like

  • Category1
    • Sub Category 1
      • Entry / Entry
    • Sub Category 2
      • Entry / Entry
  • Category 2
    • Sub Category 3
      • Entry / Entry

Etc.

Tried with grouping and even tried with a plugin - https://github.com/timkelty/groupby-craft But no luck so far, help greatly appreciated.

  • Two questions – are the categories in the same category group? And how do you want to handle entries that are in multiple categories (list each entry once, or list each entry once for every category they are in)? – Mats Mikkel Rummelhoff Oct 18 '15 at 15:17
  • 1.) Same category group - yes, 2.) Entries will only be in one category and always in a sub category (an entry would never just be in a top level category) Thanks for reply, look forward to get this working. – John Macpherson Oct 18 '15 at 20:03
4

You can get the categories to display hierarchically pretty efficiently using the {% nav %} tag:

{% set categories = craft.categories.group('myGroup').find() %}

<ul>
  {% nav category in categories %}
    <li class="category">
      {{ category.title }}
      {% ifchildren %}
        <ul>
          {% children %}
        </ul>
      {% endifchildren %}
    </li>
  {% endnav %}
</ul>

Then, you can add in the related entries like so:

<li class="category">
  ...
  {% endifchildren %}

  {# Are we on the second level of categories? #}
  {% if category.level == 2 %}
    {# Look for any entries that are related to this category #}
    {% set entries = craft.entries.relatedTo({
      targetElement: category,
      field: 'myCategoriesField'
    }) %}
    {# If there are any, then output them in a nested UL #}
    {% if entries|length %}
      <ul>
        {% for entry in entries %}
          <li>{{ entry.getLink() }}</li>
        {% endfor %}
      </ul>
    {% endif %}
  {% endif %}
</li>

That code suffers from the “N+1” problem though - each 2nd level category is going to need to initiate a new SQL query to fetch the related entries. So rendering this list will require N+1 SQL queries – one query to geth the initial set of categories, plus N, the total number of second-level categories. If you have 100 categories, that’s 101 SQL queries.

To solve this, you’d need to “eager-load” the related entries. Craft doesn’t support that of the box, but Mike Pepper has a nice little plugin called Fetch which makes it possible. Download and install that, and then you can do the following:

{% set categories = craft.categories.group('myGroup').find() %}
{% do craft.fetch.entries(categories, 'reverse:myCategoriesField') %}

<ul>
  {% nav category in categories %}
    <li class="category">
      {{ category.title }}
      {% ifchildren %}
        <ul>
          {% children %}
        </ul>
      {% endifchildren %}

      {# Are we on the second level of categories, and are there any related entries? #}
      {% if category.level == 2 and
            category.fetched('reverse:myCategoriesField')|length %}
        <ul>
          {% for entry in category.fetched('reverse:myCategoriesField') %}
            <li>{{ entry.getLink() }}</li>
          {% endfor %}
        </ul>
      {% endif %}
    </li>
  {% endnav %}
</ul>
  • Craft added eager-loading elements with version 2.6.2771 which means that Fetch has been depreciated. – bennobo Jun 22 '16 at 23:02
  • Craft’s built-in eager-loading support doesn’t actually help in this scenario yet though (reverse relationships). – Brandon Kelly Jun 23 '16 at 0:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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