Tomoro Logo

Sort and Group Posts by Custom Field with Unique Headings in WordPress

After much Googling I'm convinced there's no easier way to do this. So here's the answer I came up with.

Problem

I have ten posts (in a custom post type called 'Branch') that I want to sort by a custom field, in this case it's 'State'. We want to insert the custom field as a heading before the first Branch with that custom field, then list the rest of the posts without the heading. Then close the list.

Example

Queensland

  • Branch 1
  • Branch 2

New South Wales

  • Branch 3

Solution

In Textpattern the txp:if_different tag would've made short work of the problem. But this is WordPress, so it's time to roll-your-own…

The following code assumes you're using Advanced Custom Fields for the custom field. The ACF website actually provides an example on how to query and sort posts by custom fields, but nothing to separate those posts by custom field and put a heading before the first post with that custom field.

The code is commented but I'll explain in more detail here…

$args
We're looking for all posts in the custom post type called 'Branch', sorted in Ascending order, and only those containing the custom field 'State'.

$current_state = get_field('state');
What is the value this post's custom field?

if ($current_state != $previous_state)
If this post's State doesn't have the same value as the previous post, we want to include the heading and open the <ul> tag. If the next post does have the same State value, then this heading an open <ul> tag won't show. It'll just return the list item.

if ($previous_state) { echo '</ul>'; }
The very first post in this get_posts won't have the $previous_state variable, so this is ignored. What this line does is add a closing </ul> tag for the previous list, above the heading of the next list. So we're closing the last list at the start of the next list, instead of at the end of the current one … make sense?

$previous_state = get_field('state');
At the end of the post we need to set the $previous_state variable, so it can be compared in the next post. It's the comparison of current and previous states that makes it all work.

Simple!