Sort Posts by Term on Custom Post Type Archives

When building sites with WordPress I often make use of custom post types to add custom content types. I also use the custom post type archives in order to display an archive of this content. Recently I came across an issue where I wanted to be able to list posts on the archive grouped by terms from a taxonomy. Having quickly found out this was not so easy, I set about trying to find a solution.

The client site I was working on wanted to have a meet the team page. In fact this is a popular request amongst clients and something I often have to do. Therefore to achieve this I use a custom post type called person (prefixed of course). For the purpose of this post the custom post type is called wpmark_person.

This particular project was very specific in that it needed to have the people in the team split into the different sections they belonged to in the company e.g. Senior Leaders, Teachers etc. To achieve this grouping of posts I used a custom taxonomy which was named wpmark_person_type.

Now came the issue of displaying the team members. The design called for a page which was split into different sections, each section being a type of person (the different person types from my taxonomy). The people belonging to that section where then listed under those headings (as a thumbnail images) and each section was collapsed by default (I used jQuery to expand/contract the section but that is not covered here). Clicking the person type title revealed the actual people, in this case an image linking through to the person single post view.

Below is a wireframe of what the page layout for the meet the team would look like.

The wireframe provided indicated a different sorting of posts.
The wireframe provided indicated a different sorting of posts.

With custom post types you can declare an argument when register the custom post type of whether or not you want it to have an archive. This archive, like posts (your sites blog in a vanilla WordPress install) lists the posts in chronological order with the newest first, paginated according the number of posts per page set in the WordPress settings area for reading.

Essentially this was what I wanted, however I just wanted to order them differently and show them all. Instead of ordering them by date, I wanted to have them ordered by term from the person type taxonomy. Therefore all posts from one term and then all posts from the next etc.

In essence this is straight forward to do, but would require you to add lots of custom loops to the archive using WP_Query. This was something that I didn’t really want to do as it would mean that I am querying things twice. WordPress would query the person posts (as normal for the post type archive) and then I would throw this query away and get the posts again within the different terms. As I already have the posts I want and it was just a matter of re-ordering them, I decided there must be a more efficient way and went about tying to find a solution. First I posted on the support forums after Google searches revealed little.

I posted on the WordPress Support Forums for a Solution
I posted on the WordPress Support Forums for a Solution

After much trial and error and testing I landed on a solution that worked. Using the Query Monitor plugin I could see that it only added one more query to the page, which I thought was reasonable. My solution is as follows.

The first part was to make the archive show all the posts for that post type. I did not want pagination here and therefore wanted every person to be listed, albeit in their respective person types. To do this I used the excellent pre_get_posts action which allows you to change the query parameters WordPress uses before it actually queries the database for posts. Below shows the code to do this:

So now I have a post type archive that queries all the person posts. I can access these by the normal loop, however I needed to do some ordering before that. Therefore I need to get the post objects for these posts and manipulate them. I can access my posts through $wp_query->posts which is what I needed.

The next part was to create a function that would output an array of all the posts, split into the different terms they were assigned to. Essentially the output of my function would be like so:

The function that I used to do this was like so:

Lets talk through what this is doing. First we set some default args, which can of course be changed if passed through to the function as an array of parameters. We then create an empty output array to fill with all the terms and posts.

The first real part in the function is to get all the terms from the taxonomy (in this case are person type taxonomy). This is actually the one additional query that is carried out on the page. When we do this we are going to order them by their ID. The one created first, is shown first although we could order by name etc. We are also going to just return the term names here to keep the query as light as possible as the name is all we need to output on the page.

We then loop through each term, adding the term name as a key to our output array. Next we loop through all the posts (remember these were queried in the normal process of using a post type archive and we passed them to this function using $wp_query->posts). Inside this loop we get all the terms this post has (it should only be one as the UI for this taxonomy only enable one term to be assigned) and then push this post object into an array inside our output array for this term. Finally we return the entire array, which is now sorted into terms and posts.

Back on the post type archive template now we can use this code to output the posts.

Summarising the above code, we loop through the top level elements of the array first outputing the term name as a title. We then do a second loop through to output the posts within that term.

The output of the page looks lie this:

Custom Post Types Archive with Posted Ordered By Term
Custom Post Types Archive with Posted Ordered By Term

So there you have it, if you ever want to sort posts by a specific taxonomy term of a post type archive you can do it with just one additional query.

2 responses

  1. Jay Pick Avatar
    Jay Pick

    Thanks for sharing this. Great write up and works a treat!

  2. Luyen Dao Avatar

    Hi Mark,

    After spending an hour Googling, i came upon your excellent post. Let me be the first to say, thank you – it was very helpful, especially on a night when my head isn’t clear and trying to figure this out.

    I have one question for you, this is probably basic array PHP – but i’m not very clear how this works exactly:
    $output[ $term->name ][] = $person;

    How does the $term->name get matched in the terms array using this syntax? It’s obvious that it works, just curious how. Can you point me to some resources on this, I’m rudimentary at procedural PHP and still learning!

Leave a Reply

Your email address will not be published. Required fields are marked *