Categories, Tags, Terms Custom Order

WordPress doesn’t allow to retrieve categories or terms order by id. You have probably seen, while doing a post query ( get_post(), WP_Query() ), if we set post__in argument with an array of post id, the query exactly order the results by given ids order. But, when using get_terms(), if we pass include argument, the returning result doesn’t not come at the order term ids was passed. So, i figured a solution digging the core codes on the file wp-includes/taxonomy.php.

We can use any of two hooks available, get_terms_orderby or terms_clauses.

Terms Order by provided ids using terms_clauses hook

add_filter( 'terms_clauses', 'w4dev_terms_clauses', 10, 3 );
function terms_clauses( $clauses, $taxonomies, $args )
{
    if( 
        isset($args['include']) 
        && !empty($args['include']) 
        && isset($args['orderby_include']) 
        && $args['orderby_include'] )
       {
            $ids = implode(',', array_map('absint', $args['include']) );
            $clauses['orderby'] = "ORDER BY FIELD( t.term_id, $ids )";
        }
        return $clauses;
    }
}

Now, the above code just extends the get_terms() functionality to order by passed ids. To use this on your query, you will need to set an additional argument orderby_include => true. We can not define the default orderby argument as invalid orderby argument will automatically be remove before any useful hook is available.


Terms Order by provided ids using get_terms_orderby hook

Processing using this hook is the same as previous. But here i tried to use an fixed condition – “If include argument is defined, and orderby is left empty, we will order by include ids automatically”.

add_filter( 'get_terms_orderby', 'w4dev_get_terms_orderby', 10, 2 );
function w4dev_get_terms_orderby( $orderby, $args )
{
    if( 
        isset($args['include']) 
        && !empty($args['include']) 
        && empty($orderby) 
       {
            $ids = implode(',', array_map('absint', $args['include']) );
            $orderby = "FIELD( t.term_id, $ids )";
        }
        return $orderby;
    }
}

I haven’t noticed any difference using any of these two hooks, none with performance or with compatibility.

An Example

$get_tags = get_terms( 'post_tag', array( 
    'include' => array(21,11,31), 
    'orderby_include' => true, 
    'hide_empty' => false 
));
// this should return tags at the order 0 - 21, 1 - 11, 2 - 31.

Discussion