62

My question is simple, I'm using WP_Query to retrieve some custom type posts filtering by a taxonomy using tax_query.

Now my problem is I would like to orderby the taxonomy, but from documentation and searching on the web I can't find a solution.

The orderby in WP_Query lets you order by a bunch of fields even custom meta fields but it doesn't seem to support taxonomy.

Any pointers in the right direction?

Thank you all.

13 Answers 13

16

No, it is not possible to order by taxonomy, because from a certain type of standpoint, that doesn't actually make much sense.

Taxonomies are ways to group things together. So the point of having a taxonomy on posts would really be to have terms in that taxonomy that are shared between posts. If a taxonomy had terms that were only used on one post each, then that would make the taxonomy kind of pointless. And if the terms were shared like they should be, then ordering by it wouldn't produce anything particularly useful.

What you should be using in such a situation is the post meta. You can order by post meta, and it's unique to each post.

Edit: That said, you can order by taxonomy by making a custom SQL query using a filter, you just can't do it from a unmodified WP_Query: http://scribu.net/wordpress/sortable-taxonomy-columns.html

However, if you're having to resort to doing this sort of thing, then your data design structure is wrong in the first place. "Terms" in the taxonomy are not actual "data". The terms themselves have no inherent meaning, they're just labels for the particular grouping that they're describing. If you're treating them as meaningful data, then you have an underlying design flaw.

Taxonomies group things by assigning terms to them. That grouping is the whole point of taxonomies, the terms are just pretty faces on the grouping. If you have meaningful metadata to assign to a post, then you should be using the post meta for it instead. And that you can order by, because post meta uses both keys and values to store information. With a taxonomy, you're really only storing keys, with their values being the posts grouped together by that term.

Things are easier in the long run if you use the correct approach for it. While I'm not saying that you can't do something strange with taxonomy, you're just making things harder for yourself in the long run by using it wrong.

14
  • Hello Otto, thank you for the answer. I see your point and maybe I am going the wrong way with this. In my example a site of tv shows, I have taxonomy for series 1, serie 2, serie 3 etc.. So I can group all the different tv shows by series number. Then I have the same for episodes, Episode 01, Episode 02, etc.. What I would like is when showing a list of all the episodes to be order by episode and serie. I will analyse then post meta and custom fields. Thank you Otto.
    – yeope
    Commented Apr 8, 2011 at 16:35
  • @yeope your taxonomy should be series and your terms should be series 1, series 2 etc. With episodes I'm assuming a series contains multiple episodes so it could use the same taxonomy, "series" and if they are hierarchal then episode 1, episode 2 etc would have the parent term "series x". Then you could query a whole series in order with the episodes falling in line where they should.
    – Chris_O
    Commented Apr 8, 2011 at 19:17
  • 3
    Using a taxonomy for episodes doesn't make much sense, actually, because the grouping is worthless. Think about it, if you have "episode 1" as a term, then you're grouping episode 1 with every other episode 1 from every other TV show. The episode and series numbers make more sense as post_meta, because they're specific to that particular show, and not useful as a group. The name of the TV show would be useful as a term in a tv-show taxonomy, because then you're grouping the show as a whole together.
    – Otto
    Commented Apr 15, 2011 at 19:28
  • 1
    Otto followed this up with an interesting blog post: When to (not) use a Custom Taxonomy.
    – Jan Fabry
    Commented May 5, 2011 at 6:55
  • 1
    Sorry @Otto, I only now notice your answer... I totally agree with your semantic points and logic. But in my context, "sorting by" would be mainly an ergonomic matter. Visually grouping the projections by "festival" may help importantly enough the user to get a visual resume based on the most important factor for him, this at a glance. Thus, not sure I can achieve this somehow else than by "sorting" which I agree is not an appropriate term...
    – ClemC
    Commented Aug 19, 2017 at 19:26
71

The accepted answer for this question is unacceptable. It is illogical to assume that ordering by tax "doesn't make sense". The answer he gave doesn't make sense.

Consider having a menu post type. Then you have a custom tax of "FoodCategories". The FoodCategories tax has "Breakfast", "Lunch" and "Dinner" terms. If you submit a query utilizing the tax_query param, you now have a result set with all of the terms, however they are ordered by post date.

In order to get the correct order out of these, relative to their terms, and then to display on the front end appropriately by separating the posts into their various categories, you have to loop through the result set, then query each individual post within the result set to find it's terms and compare to the current term, filter into an array and continue throughout. Then you have to again loop through the new array for display. This is not productive.

It would be nice if WP had a "tax__in" orderby option as it does a "post__in" one, but since it does not, you either have to do the above ridiculous process; customize the query yourself by way of 'posts_orderby' filter and 'posts_join' filter in order to adjust the orderby method and add the term to the result set, respectively; or you have to make a new query for each term you are filtering for within the html sections relative to those terms.

The most efficient would be to change the query string by way of filters. The easiest would be to do three separate queries. The WP API should be handling ordering by tax, or any restrictive query parameters. If you are restricting a query based on certain conditions, there is a high probability that many will have need to order by those same conditions.

7
  • 2
    Sorry, but you are wrong. Ordering by taxonomy doesn't make any sense in your case either. What do you want to show? All the Breakfasts first, followed by all the Dinners, then all the Lunches? You should select what you want and the order in which you want it, but the taxonomy is just a grouping label. It's not meaningful "data" that you should be ordering by. If it is, then it shouldn't be a term in a taxonomy, you should make it a post-meta instead.
    – Otto
    Commented Oct 20, 2014 at 11:57
  • 29
    C'mon, of course there are going to be some instances in which you will want to order posts by taxonomy term. Another example is a Movie post type with a Rating taxonomy. In a list of movies, it's very easy to imagine people wanting to order a list of movies by rating so all G-rated, then PG-rated, etc. movies appear at the top. (In this and the meal example they could be ordered by term_id instead of name.) There's a large gray area of instances where you're probably best served by a taxonomy and not meta, but it's probably also helpful for that taxonomy to be order-able. Commented Mar 10, 2015 at 20:51
  • 3
    PG and G ratings and such are a good taxonomy choice, except that they are data about specific movies. Thus, they're meta. They're data, not categories. Just having a limited number of choices does not a taxonomy make. If it needs sort by, then either make it meta, or force the sort by the taxonomy via taxonomy specific code. BTW, NC17 comes after PG. So, you need code to do that ordering anyway.
    – Otto
    Commented Nov 10, 2015 at 1:05
  • 1
    I know I'm late to the party with this comment, but just bumped into this. Ordering by taxonomy can make sense in some situations. We have job listings on one project as a post type and then State and City in which the job is are taxonomies. We want them to be easily grupable (show all jobs in a state or show all jobs in a city) so taxonomy was the best solution. At the same time there is general job search where we want to sort them first by title, then by state, then by city. Commented Oct 23, 2018 at 10:39
  • 2
    Another use case: A client has a bunch of articles, each of which has a category. The client wants there to be a page listing all the articles, which can be sorted alphabetically, by date, or by category. Categories can also be filtered, but listing all the articles by category alphabetically is not that crazy of a use case and you see it pop up pretty often.
    – W Biggs
    Commented Jun 6, 2019 at 16:06
19

Yes, but it is pretty involved...

Add to functions.php in your theme:

function orderby_tax_clauses( $clauses, $wp_query ) {
    global $wpdb;
    $taxonomies = get_taxonomies();
    foreach ($taxonomies as $taxonomy) {
        if ( isset( $wp_query->query['orderby'] ) && $taxonomy == $wp_query->query['orderby'] ) {
            $clauses['join'] .=<<<SQL
LEFT OUTER JOIN {$wpdb->term_relationships} ON {$wpdb->posts}.ID={$wpdb->term_relationships}.object_id
LEFT OUTER JOIN {$wpdb->term_taxonomy} USING (term_taxonomy_id)
LEFT OUTER JOIN {$wpdb->terms} USING (term_id)
SQL;
            $clauses['where'] .= " AND (taxonomy = '{$taxonomy}' OR taxonomy IS NULL)";
            $clauses['groupby'] = "object_id";
            $clauses['orderby'] = "GROUP_CONCAT({$wpdb->terms}.name ORDER BY name ASC) ";
            $clauses['orderby'] .= ( 'ASC' == strtoupper( $wp_query->get('order') ) ) ? 'ASC' : 'DESC';
        }
    }
    return $clauses;
}

    add_filter('posts_clauses', 'orderby_tax_clauses', 10, 2 );

This is frankensteined from some found stuff and some stuff I did myself. Explaining is pretty tough, but the bottom line is with this running, you may put ?orderby=(taxonomy query var)&order=ASC (or DESC) and she'll take right off!

6
  • Thank you Drew, I will give a go and try to run that SQL, need to edit a bit, but it might work. My only problem now, is I might be going on the wrong directions as pointed by Otto. Thank you Drew. EDIT- No need to edit I can see where it needs the tweaking :) Thank you
    – yeope
    Commented Apr 8, 2011 at 16:45
  • If you grabbed it within the last two minutes, it won't work, go ahead and grab it now, I fixed it. It was set for two specific taxonomies, I improved the code to work on all registered taxonomies. Commented Apr 8, 2011 at 16:48
  • thank you once again. Just in case I tried your solution and it kind of works. Also if someone else wants to use it you need to change add_filter('posts_clauses', 'orderby_tax_clauses', 10, 2 ); to add_filter('posts_clauses', 'todo_tax_clauses', 10, 2 ); Thank you :)
    – yeope
    Commented Apr 8, 2011 at 22:29
  • Yes, this is now fixed in the code block, I took this from a project I'm working on and forgot to change the name of the function even though I changed it in the hook. Commented Apr 9, 2011 at 6:39
  • 1
    Do you know if it's possible to order the taxonomies by ID instead of the name? I'm trying to get the same result ordering the taxonomy groups by ID Commented Jul 16, 2013 at 22:43
13

I'm coming late to the game here, but there is a simpler more WordPressy way of doing this.

Build out your tax query like normal.

$tax_query = array();
$tax_query['relation']="OR";
$tax_query[] = array(
    'taxonomy' => 'product_cat',
    'field'    => 'slug',
    'terms'    => $cat_terms,
);
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;

Set up your args for query_posts or WP_Query

$args = array(
    'post_type'=>'post',
    'posts_per_page'=>12,
    'paged'=>$paged,
    'tax_query' => $tax_query,
);

Before you make your query_posts / WP_Query call, hook into the orderby filter and override it

add_filter('posts_orderby', 'edit_posts_orderby');
function edit_posts_orderby($orderby_statement) {
    $orderby_statement = " term_taxonomy_id ASC ";
    return $orderby_statement;
}
query_posts($args);
remove_filter('posts_orderby', 'edit_posts_orderby');

don't forget to remove filter afterwards...

this works b/c the tax_query creates the joins etc for you, you just need to order by one of the fields from the join.

5
  • 2
    Any idea on how to order by name instead of term_taxonomy_id? changing term_taxonomy_id in orderby_statement throws errors
    – tehlivi
    Commented Oct 4, 2016 at 21:52
  • 4
    This is the correct answer for anyone that is interested!
    – Mayra M
    Commented Jun 21, 2018 at 10:43
  • 1
    @tehlivi this method doesn't work for ordering by name because the name is in the wp_terms table. WordPress seems to cache the taxonomy terms, so even if your tax query searches by slug or name, which are also on the wp_terms table, WordPress runs those through its cached list and trades them out for IDs stored at term_relationships.term_taxonomy_id, so it doesn't have to query the wp_terms table as part of the main query. Which means neither name nor slug are included in the resulting SQL query. You have to add joins.
    – Ethan C
    Commented Oct 25, 2021 at 20:34
  • @EthanC yo man thanks for your detailed response, and hopefully if anyone finds this in the future, they will find your response useful. But for myself, it certainly is hard to remember anything from 2016. I think I ended up running the array through a sort function after the query completed. I'm sure that whatever developer that took over that project after I left the company hates me. Ha.
    – tehlivi
    Commented Oct 26, 2021 at 21:17
  • 1
    @tehlivi Yeah, I ended up ignoring the main query entirely in case some posts were assigned more than one term. Opted instead to run get_terms() and run get_posts for each term. Added ~ 15 queries to the page load, but gets the designer the result they wanted.
    – Ethan C
    Commented Oct 27, 2021 at 23:03
5

I'm not sure why all solutions here are pretty much overkilling it. OK, it's half a decade ago, but I'm just running the following code currently and it works:

   <?php // Default
    $wheels_args = array(
        'post_type' => 'wheels',
        'posts_per_page' => '96',
        'orderby' => 'taxonomy, name', // Just enter 2 parameters here, seprated by comma
        'order'=>'ASC'
    );
    $loop = new WP_Query($wheels_args);
    ?>

This will sort the taxonomies of your CPT first by it's taxonomy in alphabetical order and within these taxonomy groups als by alphabetical order.

4
  • @yeope Why is this the accepted answer!? thank god I scrolled Commented Oct 21, 2019 at 23:17
  • 3
    couldn't get this to work. can you point to a site with some explanation to that? nothing here, that backs your code up
    – honk31
    Commented Apr 9, 2020 at 17:15
  • 1
    Doesn't parse, especially with this comma, and is ignored by WP (6.x).
    – Picard
    Commented Sep 2, 2022 at 14:05
  • This worked for me, wp 6.5.4. You must adjust the value after the comma, e.g. if your taxonomy were post_tag you'd literally write 'orderby' => 'taxonomy, post_tag',. Commented Jun 24 at 16:21
2

Well, I’d like to expose my experience in sorting custom post types by category/taxonomy.

THE WEB

  1. A travel agency web site running on WordPress
  2. Main content on custom post type called ‘ruta’
  3. Taxonomy with this structure Type-of-travel > continent >country

THE CASE

In archive category list pages, the client wanted the posts to be sorted by

  1. The continent, ordered by number of routes on each one.
  2. The country, ordered alphabetically.

THE STEPS

First, I catch the request from the unmodified archive page query which happened to be like this:

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID 
FROM wp_posts 
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) 
WHERE 1=1 
AND ( wp_term_relationships.term_taxonomy_id IN (5,6,7,8,9,10,11,12,13,15,16,17,18,19,20,21,22,23,25,26,28,29,31,32,33,35,38,95,101,102,193) )
AND wp_posts.post_type IN ('ruta', 'nav_menu_item') 
AND (wp_posts.post_status = 'publish' OR wp_posts.post_author = 45 
AND wp_posts.post_status = 'private') 
GROUP BY wp_posts.ID 
ORDER BY wp_posts.post_date DESC LIMIT 0, 20

Second, I edited the sql code in Sequel Pro against the database to conform my needs. I come out with this (yes, probably it can be improved: my knowledge on MySQL is not outstanding):

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID, tt1.parent AS pare,
    (
    SELECT COUNT(*) 
    FROM  wp_posts
    INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
    INNER JOIN wp_term_taxonomy AS tt1 ON ( tt1.term_taxonomy_id =      wp_term_relationships.term_taxonomy_id )
    INNER JOIN wp_term_taxonomy AS tt2 ON ( tt2.term_taxonomy_id =  tt1.term_taxonomy_id )
    WHERE 1=1  
    AND tt1.parent = pare
    ) AS Total
FROM  wp_posts
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
INNER JOIN wp_term_taxonomy AS tt1 ON ( tt1.term_taxonomy_id =      wp_term_relationships.term_taxonomy_id )
INNER JOIN wp_terms ON ( tt1.term_id = wp_terms.term_id )
WHERE 1=1  
AND ( wp_term_relationships.term_taxonomy_id IN (5,6,7,8,9,10,11,12,13,15,16,17,18,19,20,21,22,23,25,26,28,29,31,32,33,35,38,95,101,102,193) ) 
AND wp_posts.post_type IN ('ruta', 'nav_menu_item') 
AND (wp_posts.post_status = 'publish' OR wp_posts.post_author = 45 
AND wp_posts.post_status = 'private') 
GROUP BY wp_posts.ID 
ORDER BY
total DESC,
wp_terms.name  

Third, I hooked the query on the functions.php file with three filters: posts_fields, posts_join and posts_orderby

The code in functions.php:

function xc_query_fields( $fields ) {

   $fields = "wp_posts.ID, wp_posts.post_title, wp_terms.name, tt1.parent AS pare,
    (
    SELECT COUNT(*) 
    FROM  wp_posts
    INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
    INNER JOIN wp_term_taxonomy AS tt1 ON ( tt1.term_taxonomy_id = wp_term_relationships.term_taxonomy_id )
    INNER JOIN wp_term_taxonomy AS tt2 ON ( tt2.term_taxonomy_id = tt1.term_taxonomy_id )
    WHERE 1=1  
    AND tt1.parent = pare
    )
    AS Total";
     return $fields;
}


function xc_query_joins( $join ) {
$join .= "INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
   INNER JOIN wp_term_taxonomy AS tt1 ON ( tt1.term_taxonomy_id = wp_term_relationships.term_taxonomy_id )
   INNER JOIN wp_terms ON ( tt1.term_id = wp_terms.term_id )";
 return $join;
}


function xc_query_orderby( $join ) {
    $join = "total DESC, wp_terms.name ";
    return $join;
 }

Finally I triggered the filters from the pre_get_post hook according to some conditions

function filtra_queries( $query )
{

  if (  is_archive()  && $query->is_main_query() && !is_admin()  ) {

$rutes = array('viajes-privados', 'asia', 'africa', 'oceania', 'america', 'oriente-proximo');

if  ( in_array( $query->get('category_name'), $rutes ) ) 
  {
  add_filter( 'posts_fields', 'xc_query_fields' );
  add_filter( 'posts_join', 'xc_query_joins' );
  add_filter( 'posts_orderby', 'xc_query_orderby' );
}// end if in_array

  }// end if is_archive

}
 add_filter('pre_get_posts', 'filtra_queries');

Hope this can help somebody

1
  • Nice work, ridiculous that is took this quantity of code to sort something by a taxonomy. Huge problem with WP.
    – serraosays
    Commented Jul 20, 2017 at 23:27
2

I had a very similar problem that I dealt with: I want to order a custom post-type archive (magazine articles) by a custom taxonomy (issues). I never do direct SQL queries on my site - and usually if you are like these other answers - you need to rethink your approach.

PROBLEMS:

1) Wordpress doesn't allow you to order taxonomies in any intelligent way.

2) Wordpress just doesn't allow orderby to be use taxonomies on post-type WP_Query (as spelled out by Otto).

SOLUTIONS:

1) Sorting taxonomies is best accomplished by the Custom Taxonomy Order NE plugin at the moment. It allows you to order the taxonomy via WYSIWYG in wp-admin which isn't how I would do it but I haven't found anything better.

When you setup the plugin, you'll get something similar to what I've done here. Make note of the option Auto-sort Queries of this Taxonomy - set this to Custom Order as Defined Above; this gets you the ordering you need. Screenshot:

Custom Taxonomy Order NE display

2) With a sorted taxonomy in place, you can now create a series of WP_Query calls that run through each term, effectively creating an archive ordered by the taxonomy. Use get_terms() to create an array of all tax terms, then run a foreach over each term. This creates a WP_Query for each term item that will return all posts for a given term, effectively creating an archive ordered by taxonomy term. Code to make this happen:

  // Get your terms and put them into an array
  $issue_terms = get_terms([
    'taxonomy' => 'issues',
    'hide_empty' => false,
  ]);

  // Run foreach over each term to setup query and display for posts
  foreach ($issue_terms as $issue_term) {
    $the_query = new WP_Query( array(
      'post_type' => 'post',
      'tax_query' => array(
        array(
          'taxonomy' => 'issues',
          'field' => 'slug',
          'terms' => array( $issue_term->slug ),
          'operator' => 'IN'
        )
      )
    ) );

    // Run loop over each query
    while($the_query->have_posts()) :
      $the_query->the_post();

      // YOUR TEMPLATE OUTPUT FOR EACH POST

    endwhile;
  }

Related reading on this site: Display all posts in a custom post type, grouped by a custom taxonomy

1

Here's the solution I've used for this particular problem. This solution is for extreme cases where it is both not possible to use a pre_get_posts filter and there is existing pagination on the query (ie: WooCommerce):

global $wpdb;

$taxonomies = array('my-tax-1', 'my-tax-2', 'my-tax-3');

$orderby = "'".implode("', '", array_keys($taxonomies))."'";
$id_sql = $GLOBALS['wp_query']->request;

$id_sql = preg_replace('/LIMIT\s+\d+\s?,?\s\d*/', '', $id_sql);
$id_sql = str_replace('SQL_CALC_FOUND_ROWS', '', $id_sql);

$term_sql = "SELECT
  tt.taxonomy AS `taxonomy`,
  t.name AS `term_name`,
  t.slug AS `term_slug`,
  count(*) AS `term_count`
FROM ({$id_sql}) p 
JOIN wp_term_relationships tr
  ON p.ID = tr.object_id
JOIN wp_term_taxonomy tt
  ON tr.term_taxonomy_id = tt.term_taxonomy_id
JOIN wp_terms t
  ON tt.term_id = t.term_id
WHERE tt.taxonomy IN ({$orderby})
GROUP BY t.slug
ORDER BY
  FIELD(tt.taxonomy, {$orderby})"; // Add further specific ordering here

$results = $wpdb->get_results($term_sql, ARRAY_A);

I've used this to create a nav menu ordered by taxonomy, term, and count of posts-per-term.

If you simply want the posts then change the query to SELECT p.* and GROUP BY p.ID

1

i like to sort my terms manually, so i use a plugin to do so. and i'm a fan of the pre_get_posts filter, so i took the proper working example of Drew Gourley and made it work with that. so this is some kinda special case, but i'm posting this anyhow, in case it helps someone. following code goes into functions.php or a custom plugin.

first lets start of with the filter. we order the custom post type music by custom taxonomy style

function so14306_pre_get_posts($query)
{
    if (is_admin()) :
        return;
    endif;

    if ($query->is_main_query()) :
        if (is_post_type_archive('music')) :
            $query->set('orderby', 'style');

        endif;
    endif;
}

add_action('pre_get_posts', 'so14306_pre_get_posts');

then we call the post_clauses filter:

function so14306_posts_clauses($clauses, $wp_query)
{
    global $wpdb;

    if (isset($wp_query->query_vars['orderby']) && $wp_query->query_vars['orderby'] === 'style') {
        $orderby = $wp_query->query_vars['orderby'];
        $clauses['join'] .= <<<SQL
LEFT OUTER JOIN {$wpdb->term_relationships} ON {$wpdb->posts}.ID={$wpdb->term_relationships}.object_id
LEFT OUTER JOIN {$wpdb->term_taxonomy} USING (term_taxonomy_id)
LEFT OUTER JOIN {$wpdb->terms} USING (term_id)
SQL;
        $clauses['where'] .= " AND (taxonomy = '{$orderby}' OR taxonomy IS NULL)";
        $clauses['groupby'] = "object_id";
        $clauses['orderby'] = "GROUP_CONCAT({$wpdb->terms}.term_order ORDER BY {$wpdb->terms}.term_order ASC) ASC";
        $clauses['orderby'] .= ", {$wpdb->posts}.post_name ASC";
    }

    return $clauses;
}

add_filter('posts_clauses', 'so14306_posts_clauses', 10, 2);

now all you need to do, is sort your taxonomies with the following plugin: Simple Custom Post Order. This plugin is mandatory for this solution, since it adds the column term_order to the database!

and this line here: $clauses['orderby'] .= ", {$wpdb->posts}.post_name ASC" orders the posts by title, so the full sorting of the above solution is: taxonomy term => post title.

3
  • Thank you so much, this is very helpful. It's great to have a way to order visually. Do you know why the "OR taxonomy IS NULL" part is there? It seems like something that isn't necessary. Commented Oct 6, 2021 at 19:14
  • It displays the first term properly but after those posts the next term does not logically follow the menu order as set out with SCP Order. I'm wondering if it's because I'm trying to do it with products, and WooCommerce already has an attribute order...maybe the two are conflicting. In the DB though the IDs in the term_order column are correct... Commented Oct 6, 2021 at 19:32
  • Changing the GROUP_CONCAT orderby line to the following seems to help: $clauses['orderby'] = "{$wpdb->terms}.term_order ASC"; Commented Oct 6, 2021 at 19:44
1

A fairly simple way to do this is add in a function that says:

  1. When your post is published...
  2. Get your category's id/slug/etc...
  3. Save it as a custom meta value for your post

And then, in your loop, order your posts by that meta value.

So:

// 1
add_action( 'publish_post', 'save_and_add_meta' );

function save_and_add_meta($post_id){

        //Temporarily remove the action to avoid an infinite loop
        remove_action( 'publish_post', 'save_and_add_meta' );

        // 2            
        $category_slug = get_the_terms($post_id, 'your_taxonomys_name')[0]->slug;

        //3
        add_post_meta($post_id, 'cat_slug', $category_slug);

        //Add the action back in.
        add_action( 'publish_post', 'save_and_add_meta' );
}

Then in your WP query add into your $args:

'meta_key' => 'cat_slug',
'orderby' => 'meta_value',
'order' => 'DESC',

This works if you can find a way to restrict users to assigning only one category per post, or if your categories are mutually exclusive. If they assign more than one category, though, the post would be left out of one of them.

1
  • I am thinking this as well, although it duplicates data.
    – Sisir
    Commented Sep 22, 2021 at 20:18
0

It's like a query before the query, but won't bother if we're not not querying too many posts... The idea is to modify the main query so we don't even need to go to templates and generate new queries and loops...

function grouped_by_taxonomy_main_query( $query ) {

    if ( $query->is_home() && $query->is_main_query() ) { // Run only on the homepage

        $post_ids = array();

        $terms = get_terms('my_custom_taxonomy');

        foreach ( $terms as $term ) {
            $post_ids = array_merge( $post_ids, get_posts( array( 
                'posts_per_page' => 4, // as you wish...
                'post_type' => 'my_custom_post_type', // If needed... Default is posts
                'fields' => 'ids', // we only want the ids to use later in 'post__in'
                'tax_query' => array( array( 'taxonomy' => $term->taxonomy, 'field' => 'term_id', 'terms' => $term->term_id, )))) // getting posts in the current term
            );
        }

        $query->query_vars['post_type'] = 'my_custom_post_type'; // Again, if needed... Default is posts
        $query->query_vars['posts_per_page'] = 16; // If needed...
        $query->query_vars['post__in'] = $post_ids; // Filtering with the post ids we've obtained above
        $query->query_vars['orderby'] = 'post__in'; // Here we keep the order we generated in the terms loop
        $query->query_vars['ignore_sticky_posts'] = 1; // If you dont want your sticky posts to change the order

    }
}

// Hook my above function to the pre_get_posts action
add_action( 'pre_get_posts', 'grouped_by_taxonomy_main_query' );
1
  • This will Fail, you can not use get_posts or WP_Query inside pre_get_posts, it will create an infinite loop. because when you use get_posts or WP_Query, it will fire pre_get_posts hook and this will cause an infinite loop, even if you check is_main_query, it will not stop and run continuously.
    – Den Pat
    Commented Mar 23, 2022 at 10:31
0

Some answers quite complex, I wrote this solution which is pretty basic to understand (i think/hope):

$args = array( 'post_type' => 'Teammember','posts_per_page' =>  -1);
$loop = new WP_Query($args);
if ( $loop->have_posts() ) {
     while ( $loop->have_posts() ) {
         $loop->the_post();
         $id = get_the_id();
         $name = get_the_terms( get_the_ID(), 'teammember-category' );
         $sort[$id] = $name[0]->name;
    }
}
wp_reset_postdata();
asort($sort);
$result = array_keys($sort);

Now the variable $result contains al the ids of the posts, ordered ascending by custom taxonomy name. Caution: if a post contains multiple categories, a different solution is needed.

You can use the ids in a foreach function, something like this:

foreach($result as $id){
$image = get_the_post_thumbnail($id);
...etc.
} 

or you can make a second query like this:

$loopnew = new WP_Query(array('post_type' => 'Teammember','post__in'=> $result));
if ( $loopnew->have_posts() ) {
     while ( $loopnew->have_posts() ) {
         $loopnew->the_post();
         ...etc.
     }
 }
-1

It's kinda anoying Wordpress won't let you do that and forces you to basically have two fields with the same information..

BUT i'll post here my solution. The idea is to pass all taxonomies terms selected to post meta info.

(Careful you need to tweek it if you're taxonomy allows multiple values)

//add this hook to automaticly save terms as post meta data when you save a post
add_action('save_post', 'add_custom_taxonomies_as_post_meta', 10, 1);
function add_custom_taxonomies_as_post_meta($id)
{
  $current_post = get_post($id);
  $taxonomies  = get_object_taxonomies($current_post);
  foreach ($taxonomies as $tax) {
    $post_terms = get_the_terms($id, $tax);
    $term = $post_terms[0];
    add_post_meta($id, $tax,  $term->name, true);
  }
}

After placing this in your functions.php you can use meta_key and orderby as usual to order your posts like this:

   'meta_key' => YOUR-TAXONOMY,
    'orderby'    => array(
      'meta_value' => 'ASC'
    )

Hope this is useful to anyone.

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.