I'm picking a season category (spring, summer, fall, winter) in a global variable that should dictate what entries come to the top of non-season category pages. These entries are already on a category page, sorted by their main category. I need extra sorting to showcase the "Spring" entries, for instance. Something like this:

Without the season global variable chosen

  1. Test post 1 (Summer)
  2. Test post 2 (Spring)
  3. Test post 3 (Spring)
  4. Test post 4 (Winter)
  5. Test post 5 (Winter)

With the global variable "Spring" chosen

  1. Test post 2 (Spring)
  2. Test post 3 (Spring)
  3. Test post 1 (Summer)
  4. Test post 4 (Winter)
  5. Test post 5 (Winter)

Basically, I want to bring the entries that are using the chosen Global Variable Category to the top, the remaining entries can be sorted by postDate descending. These entries are in a Structure btw, if that matters.

Structure = 'generalContent'
Category Field = 'season'
Global Variable = 'season.season'

Simplified code of my category page.

{% paginate craft.entries.section('generalContent').relatedTo(category).order('lft desc') as entries %}
  {% for entry in entries %}
    {{ entry.title }}<br>
  {% endfor %}
{% endpaginate %}

1 Answer 1


One solution is to retrieve the entry ids for the season entries and all entries separately; subtract the 'season entry ids' from 'all entry ids' (using craft's without twig filter); then merge the two arrays back together (using twig's merge filter) in the proper order. You can then use this new id array to generate an elementCriteriaModel which can be used in your paginate expression.

{% cache %}
    {% set season = season.season.first() %}
    {% set seasonEntryIds = craft.entries.section('generalContent').relatedTo(season).order('lft desc').ids() %}
    {% set allEntryIds = craft.entries.section('generalContent').order('lft desc').ids() %}
    {% set nonSeasonEntryIds = allEntryIds|without(seasonEntryIds) %}
    {% set sortedEntryIds = seasonEntryIds|merge(nonSeasonEntryIds) %}

    {% paginate craft.entries.id(sortedEntryIds).fixedOrder(true) as entries %}
        {% for entry in entries %}
            {{ entry.title }}<br>
        {% endfor %}
    {% endpaginate %}
{% endcache %}

Since there are a lot of queries happening here, it might also be a good idea to wrap the code in 'cache' tag.

  • Douglas, everything looks great up until the sortedEntryIds. For whatever reason, it's putting them back in order in Structure's native Ascending order. Jun 9, 2015 at 21:12
  • Also, not sure if this is a hit to performance, but nonSeasonEntryIds doesn't appear to be needed. The merge filter already overrides existing keys in the array. I get the same result with this: sortedEntryIds = seasonEntryIds|merge(allEntryIds) Jun 9, 2015 at 21:47
  • Patrick Harrington (mildlygeeky on Slack) gave me the heads up that the parameter .fixedOrder(true) should be used on the {% paginate %} element. Douglas, if you want to put that in your answer, I'll mark it as answered. Jun 10, 2015 at 14:38
  • Using merge and without does not create any additional database queries on their own, so shouldn't be any hit to performance either way. I am surprised, however, to hear that the merge filter is overriding existing array items, as we are simply dealing with an array of numbers – and there are many reasons why you might want duplicate numbers in your array. I presume this is because, as you say, there is an underlying key there somewhere — I'll watch out for that. Regarding fixedOrder(true) — great catch —I updated the answer. Regardless — I'm glad you got it working. Jun 10, 2015 at 16:16

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.