Recently I needed to implement a way to return custom taxonomy terms sorted by their latest posts.

For example, imagine we have a movie review site, and we use the default category taxonomy as “Movie Categories”. Maybe they will have the following taxonomy terms:

  • Action
  • Horror
  • Drama
  • Kids

Getting taxonomy terms with get_terms()

We can easily get the category taxonomy terms by using get_terms():

$categories = get_terms(['taxonomy' => 'category']);
foreach($categories as $term) {
  echo "<li>{$term->name}</li>";
}
/**
 * Output:
 * <li>Action</li>
 * <li>Horror</li>
 * <li>Drama</li>
 * <li>Kids</li>
 */

Sorting get_terms() by their latest post dates

However from here, things get a little difficult. Since we want to sort not by the term, but by the latest post for each term, We need to somehow 1. Find the latest post for each term, 2. Get the post date, and finally 3. Use the post date to sort our terms by.

Looking into this, I did find this post with a similar question, however for my goal, I wanted to use get_terms() with the terms already sorted. This way I can use this easily anywhere the terms may be needed.

For my solution, we utilize the get_terms filter. With it, we can filter and modify any get_terms() calls, allowing us to use this sorting as our default sort for categories:

add_filter('get_terms', function(array $terms, array|null $taxonomies,array $get_term_args, \WP_Term_Query $term_query) {
...
}, 10, 4);

To let the filter know that we want to use our custom sorting, we will add our own custom argument to get_terms(). We don’t want to interfere with how WP works so we can’t use orderby. Let’s use sortby:

$categories = get_terms(['taxonomy' => 'category', 'sortby' => 'post_date']);

Now we can check for that in our filter:

...
// We only want to filter if it has our custom argument
if(empty($get_term_args['sortby']) || $get_term_args['sortby'] !== 'post_date') {
  return $terms;
}

Let’s write a function now that will retrieve the latest post date for a term. We will also check if an empty term has been passed in, in case we call get_terms() with hide_empty set to false:

function get_latest_term_post_date( \WP_Term|null $term ) {

  // Return an empty string if we don't have a term, or term count is 0
  if(!$term || $term->count === 0) return '';

  // Grab the latest post
  $latest_post = get_posts([
    'posts_per_page' => 1,
    'post_type' => 'post',
    'tax_query' => [
      [
        'taxonomy' => 'category',
        'field' => 'slug',
        'terms' => $term->slug,
      ]
    ],
    'orderby' => 'date',
    'order' => 'DESC',
  ]);

  // Return the post date
  return $latest_post[0]->post_date;
}

Next, we want to add a custom property to the terms to store our latest post date. Fortunately, the WP_Term class supports AllowDynamicProperties , meaning we can go ahead and add custom properties without any issues:

...
// Add a new field to each term
foreach($terms as &$term) {
  $term->latest_post_date = get_latest_term_post_date($term);
}

Finally, we can sort the terms by our custom property and return the terms:

...
// Sort the terms by $term->latest_post_date
usort($terms, fn($a, $b) => ($a->latest_post_date > $b->latest_post_date ? -1 : 1));

return $terms;
...

With that, we are able to call get_terms() and sort our terms by their latest posts. It should be easy to modify for other taxonomies or post types.

Whole Code to sort category by their latest post date

add_filter('get_terms', function(array $terms, array|null $taxonomies,array $get_term_args, \WP_Term_Query $term_query) {

  // We only want to filter if it has our custom argument
  if(empty($get_term_args['sortby']) || $get_term_args['sortby'] !== 'post_date') {
    return $terms;
  }

  // Add a new field to each term
  foreach($terms as &$term) {

    $term->latest_post_date = get_latest_term_post_date($term);
  }

  // Sort the terms by $term->latest_post_date
  usort($terms, fn($a, $b) => ($a->latest_post_date > $b->latest_post_date ? -1 : 1));

  return $terms;
}, 10, 4);

function get_latest_term_post_date( \WP_Term|null $term ) {

  // Return an empty string if we don't have a term, or term count is 0
  if(!$term || $term->count === 0) return '';

  // Grab the latest post
  $latest_post = get_posts([
    'posts_per_page' => 1,
    'post_type' => 'post',
    'tax_query' => [
      [
        'taxonomy' => 'category',
        'field' => 'slug',
        'terms' => $term->slug,
      ]
    ],
    'orderby' => 'date',
    'order' => 'DESC',
  ]);

  // Return the post date
  return $latest_post[0]->post_date;
}