3

I found a solution to grouping entries by category here which is working perfectly but I need the categories in the order they were arranged in the categories tab in the Admin panel.

Currently, the categories are outputting in the order they were entered but they've been re-ordered since then.

I tried using .fixedOrder(true) in a few places but nothing good happened. This is the current code:

{% set entries = craft.entries({
  section: 'projects'
}).find() %}

{% for category, catEntries in entries|group('projectCategory.first().title') %}
    <h4 class="sc">{{ category }}</h4>

    <ul class="no-bullet">
        {% for entry in catEntries %}
            <li><a href="{{ entry.url }}">{{ entry.title }}</a></li>
        {% endfor %} {# -- // for entry -- #}
    </ul>
{% endfor %}{# -- // for category -- #}
1
  • Do all projects have a category? Or do you need to show entries that are category-less? Also, should projects show up under EACH category they are related to or only be limited to the FIRST category like the code you have above does?
    – Alex Roper
    Commented Apr 16, 2015 at 21:26

2 Answers 2

3

Twig is a templating language, and in my opinion this is pushing what it's comfortably meant to do. You might want to consider handling the heavy lifting w/ PHP by writing a small business logic plugin, or take a look at third party plugins like Micheal Rog's SuperSort to help out.

That being said, if you want to go plugin-less the following should work (note that you'll need to change "myProjectCategoryHandle" to the actual handle of the category group used for the projectCategory field):

{# Pull entries, group by category field "projectCategory" #}
{% set entries = craft.entries.section( 'projects' ) | group( 'projectCategory.first().title' ) %}

{# Get all category titles for the category group "yourProjectCategoryHandle" #}
{% set categories = craft.categories.group( 'yourProjectCategoryHandle' ).titles %}

{# Loop through the category title array #}
{% for category in categories %}

    {# Get the entries grouped under the current category title (if any) #}
    {% set catEntries = entries[ '' ~ category ] | default( [] ) %}

    {# Print the category title and entries #}
    {% if catEntries | length %}
    <h4 class="sc">{{ category }}</h4>
    <ul class="no-bullet">
        {% for entry in catEntries %}
            <li><a href="{{ entry.url }}">{{ entry.title }}</a></li>
        {% endfor %} {# -- // for entry -- #}
    </ul>
    {% endif %}

{% endfor %}
3
  • That is insane! Shouldn't the default order be the order that the categories are set in the Admin CP? Seems like I should have to force the order just to make the category order not how they are arranged. I did look at SuperSort but there doesn't seem to be an option to sort by the fixer order.
    – smartpill
    Commented Apr 14, 2015 at 19:57
  • That said, I just tried it and your solution does work. So thank you.
    – smartpill
    Commented Apr 14, 2015 at 20:08
  • The problem is that the group filter has no idea about order – it just takes an array of objects (the entries) and an array of strings (the category titles) and sorts the former on whatever matches. The entries are really what determines the order in this case. Commented Apr 14, 2015 at 20:18
4

Depending on your needs, you may be able to ditch the group() filter and sort your list however you need.

Get all your entries and categories. Then use the relatedTo functions to filter those lists.

{% set categories = craft.categories.group('projectCategories') %}
{% set projects = craft.entries.section('projects').order('title').limit(null) %}

{% for category in categories.relatedTo(projects) %}
  <h3>{{ category.title }}</h3>

  <ul class="no-bullet">
    {% for entry in projects.relatedTo(category) %}
      <li><a href="{{ entry.url }}">{{ entry.title }}</a></li>
    {% endfor %}
  </ul>
{% endfor %}

One caveat might be that this solution pulls all your entries and categories. This worked for my situation where I had around 50 categories and under 200 entries. I also knew these numbers wouldn't get bigger in the future.

This also makes a few other assumptions too...

  • You DON'T need to show entries with NO related categories like this:

Category A

  • entry
  • entry

Category B

  • entry

(No Categories)

  • entry

Also, your entries can show up under EACH category they're related to, instead of just the FIRST category like your original code. For example, if Entry 1 has Category A and Category B selected, you'll get this:

Category A

  • entry 1

Category B

  • entry 1
3
  • Aside from the possible performance issue with pulling all entries & categories, this solution will print – unless I misread your code above – entries multiple times, i.e. once for each category its related to; whereas the original code will only print an entry once, even its related to multiple categories. I'm assuming the latter is wanted behaviour in this case (although if the category field has a hard 1 category limit it probably won't be an issue either way). Commented Apr 16, 2015 at 10:21
  • You are correct. That's another difference with this solution. The original code will also show entries related to NO categories whereas this won't. I'll do a little editing on my answer.
    – Alex Roper
    Commented Apr 16, 2015 at 20:27
  • Thanks Mats. You put me on the track to completely refactor my code and simplify the answer. :)
    – Alex Roper
    Commented Apr 16, 2015 at 22:05

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.