Order Posts by Taxonomy and Meta Value

It’s possible to order posts by Meta Value.

It’s possible to order posts by more than one value.

Read More

And thanks to the code below it’s possible to order by taxonomy.

But how can I alter the code below to order by taxonomy AND meta_value?

Added to functions.php

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 );

Added to index.php

<?php $args = array(
'post_type' => 'custom_post_type',
'posts_per_page' => -1,
'meta_key' => '_EventStartDate',
'orderby' => 'taxonomy_cat',
'order' => asc
);
$the_query = new WP_Query( $args );

if ( $the_query->have_posts() ) : while ( $the_query->have_posts() ) : $the_query-    >the_post(); ?>

etc. etc.

Changing this doesn’t work

'orderby' => 'taxonomy_cat meta_value',

Related posts

1 comment

  1. In your code, there is

    if ( isset( $wp_query->query['orderby'] ) && $taxonomy == $wp_query->query['orderby'] 
    

    that make your code not work with multiple order fields, because if you set

    'orderby' => 'taxonomy_cat meta_value'
    

    then $taxonomy == $wp_query->query['orderby'] is never true.

    In addition, I find that looping all taxonomy only with the aim to find the taxonomy to order by is low performant and not very reliable.

    So I edited your code a little: when you want to order for a taxonomy your arg shouldbe something like:

    'orderby' => 'taxonomy.taxonomy_cat'
    

    thanks to the 'taxonomy.' is easy recognize the taxonomy and there is no need to loop through all taxonomies.

    After that, WordPress allow to pass only one argument as 'order' argument, so when you pass multiple 'orderby' arguments, the result can be not the expected one.

    So I edited your code to accept an additional argument: 'ordertax'. Sure this is optional, so if you not pass it, the ‘order’ argument is used. It is also ignored if 'orderby' does not contain any taxonomy argument.
    If you use multiple 'orderby' arguemnts without passing 'ordertax', all the arguments are order based on 'order' argument (or default ‘DESC’), so using

    'orderby' => 'taxonomy.taxonomy_cat meta_value',
    'order' => 'ASC'
    

    means "ORDERBY taxonomy_cat ASC, meta_value ASC", using

    'orderby' => 'taxonomy.taxonomy_cat meta_value',
    'ordertax' => 'DESC'
    'order' => 'ASC'
    

    means "ORDERBY taxonomy_cat DESC, meta_value ASC".

    Now here the code:

    function orderby_tax_clauses( $clauses, $wp_query ) {
      $orderby_arg = $wp_query->get('orderby');
      if ( ! empty( $orderby_arg ) && substr_count( $orderby_arg, 'taxonomy.' ) ) {
        global $wpdb;
        $bytax = "GROUP_CONCAT({$wpdb->terms}.name ORDER BY name ASC)";
        $array = explode( ' ', $orderby_arg ); 
        if ( ! isset( $array[1] ) ) {
          $array = array( $bytax, "{$wpdb->posts}.post_date" );
          $taxonomy = str_replace( 'taxonomy.', '', $orderby_arg );
        } else {
          foreach ( $array as $i => $t ) {
            if ( substr_count( $t, 'taxonomy.' ) )  {
              $taxonomy = str_replace( 'taxonomy.', '', $t );
              $array[$i] = $bytax;
            } elseif ( $t === 'meta_value' || $t === 'meta_value_num' ) {
              $cast = ( $t === 'meta_value_num' ) ? 'SIGNED' : 'CHAR';
              $array[$i] = "CAST( {$wpdb->postmeta}.meta_value AS {$cast} )";
            } else {
              $array[$i] = "{$wpdb->posts}.{$t}";
            }
          }
        }
        $order = strtoupper( $wp_query->get('order') ) === 'ASC' ? ' ASC' : ' DESC';
        $ot = strtoupper( $wp_query->get('ordertax') );
        $ordertax = $ot === 'DESC' || $ot === 'ASC' ? " $ot" : " $order";
        $clauses['orderby'] = implode(', ',
          array_map( function($a) use ( $ordertax, $order ) {
            return ( strpos($a, 'GROUP_CONCAT') === 0 ) ? $a . $ordertax : $a . $order;
          }, $array )
        );
        $clauses['join'] .= " LEFT OUTER JOIN {$wpdb->term_relationships} ";
        $clauses['join'] .= "ON {$wpdb->posts}.ID = {$wpdb->term_relationships}.object_id";
        $clauses['join'] .= " LEFT OUTER JOIN {$wpdb->term_taxonomy} ";
        $clauses['join'] .= "USING (term_taxonomy_id)";
        $clauses['join'] .= " LEFT OUTER JOIN {$wpdb->terms} USING (term_id)";
        $clauses['groupby'] = "object_id";
        $clauses['where'] .= " AND (taxonomy = '{$taxonomy}' OR taxonomy IS NULL)";
      }
      return $clauses;
    }
    

    Use it like this:

    $args = array(
      'post_type'      => 'custom_post_type',
      'posts_per_page' => -1,
      'meta_key'       => '_EventStartDate',
      'orderby'        => 'taxonomy.taxonomy_cat meta_value',
      'ordertax'       => 'ASC',
      'order'          => 'DESC'
    );
    
    add_filter( 'posts_clauses', 'orderby_tax_clauses', 10, 2 );
    $the_query = new WP_Query( $args );
    remove_filter( 'posts_clauses', 'orderby_tax_clauses', 10, 2 );
    

    Note that use my code (just like the yours) with a WP_Query containing a taxonomy query may break things… for this reason is important remove the filter after having used it.

Comments are closed.