In our recent article on setup_postdata()
, a reader posted a good question: how to sort a fetched array of WP_Post
objects alphabetically by their terms in a given taxonomy?
In other words, if I’m working with posts of type Movie, and they’re organized in a taxonomy called Genre, then how do I output first the Movies in “Action,” and then “Comedy,” and then “Romance”—in one loop, and regardless of the actual titles of the posts themselves?
The current top answer in Google for “sort posts by taxonomy value” uses multiple get_posts()
calls within a foreach()
loop. I don’t love that solution because it implies working with multiple arrays of WP_Post
objects—rather than one, sorted array that we can do whatever we want with.
Before we dive in, an invitation. If you’re looking to better understand WordPress development in general, we’ve written the best guide to it out there:
The Best Way to Learn WordPress Development
Get Up and Running Today!
Up and Running is our complete “learn WordPress development” course. Now in its updated and expanded Third Edition, it’s helped hundreds of happy buyers learn WordPress development the fast, smart, and thorough way.Here’s what they have to say:
“I think anyone interested in learning WordPress development NEEDS this course. Watching the videos was like a bunch of lights being turned on.” -Jason, WordPress developer
“Other courses I’ve tried nearly always lack clear explanations for why WordPress does things a certain way, or how things work together. Up and Running does all of this, and everything is explained clearly and in easy-to-understand language.” -Caroline, WordPress freelancer
Now: sorting our taxonomy terms all in one loop, across a single sorted array of WP_Post
s, is a fair amount of code, but there is a good solution. That’s what we cover today.
Demo of Posts Sorted by Taxonomy Terms
This demo comes from a test site, which has two posts in two different categories:
The test site is running Twenty Seventeen. We pasted the entirety of the code from the “Full Working Code” section below into Twenty Seventeen’s index.php
template, where The Loop would normally go, as follows (see line 34):
That gives this result on the demo site’s homepage:
This is special because it’s sorting these posts alphabetically by the name of the category they’re in. If the posts were being sorted either alphabetically by their own names, or reverse-chronologically by their publication dates (as in the “Recent Posts” widget on the right), then they’d appear in the opposite order.
Sorting an Array of Posts by their Taxonomy Terms: Full Working Code
The code below does the following:
- Fetches all posts (of type
post
by default) that have a value for thecategory
taxonomy. - Sorts those posts alphabetically by the name of the first category they’re listed in.
- Outputs the permalinks and titles of each post in their sorted order.
function wpshout_fetch_posts_in_category_taxonomy() {
// Fetch posts that have a value for the 'category' taxonomy
$args = array(
'tax_query' => array(
array(
'taxonomy' => 'category',
'operator' => 'EXISTS',
)
)
);
// Return fetched posts
return get_posts( $args );
}
// Customize each of the fetched WP_Post objects: each will have a
// 'category' property containing the WP_Term object of its first category
function wpshout_add_category_term_objects_to_posts( $posts ) {
foreach( $posts as $post_index => $current_post ) :
// Get array of WP_Term category terms for the current post
$terms = get_the_terms( $current_post, 'category' );
// Save the first WP_Term object to the WP_Post object
$current_post->category = $terms[0];
// Update the $posts array with the modified WP_Post object
$posts[$post_index] = $current_post;
endforeach;
// Return array of modified WP_Post objects
return $posts;
}
// Define sorting function to sort by category name
function wpshout_sort_posts_by_category( $a, $b ) {
return strcmp(
wp_strip_all_tags( $a->category->name ),
wp_strip_all_tags( $b->category->name )
);
}
// Call just-registered functions to get sorted array of WP_Post objects,
// then output with foreach()
function wpshout_output_posts_sorted_by_category() {
$posts = wpshout_fetch_posts_in_category_taxonomy();
// Return if no results
if( ! is_array( $posts ) ) :
return false;
endif;
// Add category WP_Term object as a property to each WP_Post object
$posts = wpshout_add_category_term_objects_to_posts( $posts );
// Sort posts by category name
usort( $posts, 'wpshout_sort_posts_by_category' );
// Call global $post variable
global $post;
// Loop through sorted posts and display using template tags
foreach( $posts as $current_post ) :
// Set $post global variable to the current post object
$post = $current_post;
// Set up "environment" for template tags
setup_postdata( $post );
// Use template tags normally
echo '<div><a href="' . get_the_permalink() . '">';
the_title();
echo '</a></div>';
endforeach;
// Reset global $post variable
wp_reset_postdata();
}
// Call the outputting function wherever you want in your template file
wpshout_output_posts_sorted_by_category();
Notes on the Code
There are a few key points to notice here.
Adding a category
Property to the WP_Post
Objects
As the original question mentioned, taxonomy terms are not a default property of a WP_Post
object. That’s why sorting on them is more of a challenge than sorting by the posts’ names, publication dates, and so on.
We fixed that issue by bolting on another object—a WP_Term
object—giving all sorts of information about the first category of which each post is a member. A sample WP_Term
object looks like this:
object(WP_Term)#1143 (10) {
["term_id"]=>
int(15)
["name"]=>
string(14) "First Category"
["slug"]=>
string(14) "first-category"
["term_group"]=>
int(0)
["term_taxonomy_id"]=>
int(15)
["taxonomy"]=>
string(8) "category"
["description"]=>
string(0) ""
["parent"]=>
int(0)
["count"]=>
int(1)
["filter"]=>
string(3) "raw"
}
Each WP_Post
object, through the wpshout_add_category_term_objects_to_posts()
function, had a WP_Term
object added as its new category
property.
usort()
by Category Name
With the step above completed, we could then use the name
property of the category
property of each post. Getting the category name from a modified WP_Post
object looks like: $this_post->category->name
.
That enabled us to do an alphabetical ordering of posts by their category names using usort()
. We touch a bit more on usort()
itself in our article on setup_postdata()
.
Outputting Content
To use an array of WP_Post
objects like we would normally use a WP_Query
object, we again make use of WordPress’s lovely setup_postdata()
function.
Where to Use This Code
You can use this code in a plugin or a theme template file: anywhere where you need to fetch posts, order them by a taxonomy term, and output them onto the page.
Our quick demo above simply pasted the full demo code into a theme’s index.php
. You could also put everything but the final line into your theme’s functions.php
and then call wpshout_output_posts_sorted_by_category()
anywhere in your theme you want, or register a widget that uses this functionality, and so on.
Changing Arguments for get_posts()
As our guides to WP_Query
and get_posts()
cover, you can customize your get_posts()
arguments as you like. The below example changes the targeted post type to product
, limits the fetched results to the five most recently published posts, and changes the taxonomy to be sorted to product_cat
.
'post_type' => 'product',
'posts_per_page' => 5,
'tax_query' => array(
array(
'taxonomy' => 'product_cat',
'operator' => 'EXISTS',
)
)
The Drawback of this Way of Sorting Post Objects
The thing I wish I could change is the number of separate database queries: in addition to the single get_posts()
call, there’s one get_the_terms()
call per fetched post. If your get_posts()
call fetched a thousand posts, then that’s 1,001 extra trips to the database, which is going to hurt from a performance standpoint.
I’d be interested to hear if anyone knows of an improvement for this. One idea would be to write a single, cached query that fetches the taxonomy terms for the targeted taxonomy for all posts, and then just reference the results of that query in the rest of the code but I’m not sure how that’d work in practice.
Your Wish is Our Command
So, we do read the comments. 🙂 Understanding how to sort posts by their metadata—including their taxonomy terms—is also a pretty helpful thing to know how to do in WordPress, so hopefully you’ve understood the demo above and how it could be altered and extended. If so, congratulations, you’re Pretty Good in WordPress.
Thanks for reading! Let’s hear your questions and comments below.
I don’t know how to code it, but I’ve seen elsewhere a function written to save the category or term to a custom meta field when publishing the post. This allows that field to be used by the native orderby.