Order and Group Entries by Category

Problem

You have a bunch of entries with a category field accepting only one category and you want to order and group those entries by category.

Solution

A recent use case for this was when I had to display sponsors on a site.

Each sponsor has a sponsor level (a category). In my templates, I wanted to display the entries grouped per category and I wanted those categories to appear in the order they have in the CP. That way, if the admin changes the order of the categories, it will be reflected on the site.

{#
 # Sponsors listed by categories
 # -----------------------------
 # - create an empty array to hold our entries
 # - Get the list of categories in the order they are in in the CP
 # - For each category, get the list of entries related to that category
 # - Merge that with the "sponsors" array
##}
{% set sponsors = [] %}
{% set sponsorsCategories = craft.categories.group('sponsorLevels').find() %}
{% for category in sponsorsCategories %}
    {% set categorySponsors = craft.entries.section('sponsors').relatedTo(category).find() %}
    {% set sponsors = sponsors|merge(categorySponsors) %}
{% endfor %}


{# display sponsors #}
{% for sponsor in sponsors %}
    {% if loop.first %}<ul class="sponsorslist">{% endif -%}

        {# set category and logo #}
        {% set sponsorCategory = sponsor.sponsorLevel.first() %}
        {% set sponsorLogo = sponsor.sponsorImage.first() %}

        <li class="sponsorslist__item">
            <a href="{{ sponsor.sponsorUrl }}">
                <img src="{{ sponsorLogo.getUrl(sponsorThumb) }}" alt="{{ sponsor.title }}" class="sponsor__logo" />
                <span class="sponsor__level  sponsor__level--{{ sponsorCategory.slug }}">{{ sponsorCategory.title }}</span>
            </a>
        </li>

    {% if loop.last %}</ul>{% endif %}
{% else %}
    <p>No sponsor found</p>
{% endfor %}