WordPress doesn’t allow to fetching categories or terms order by their 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 available hooks, get_terms_orderby
or terms_clauses
.
Terms Order by provided ids using terms_clauses hook
function terms_clauses( $clauses, $taxonomies, $args ) {
if ( ! empty( $args['include'] ) && ! empty( $args['orderby_include'] ) ) {
$ids = implode( ',', array_map( 'absint', $args['include'] ) );
$clauses['orderby'] = "ORDER BY FIELD( t.term_id, $ids )";
}
return $clauses;
}
add_filter( 'terms_clauses', 'w4dev_terms_clauses', 10, 3 );
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”.
function w4dev_get_terms_orderby( $orderby, $args ) {
if ( ! empty( $args['include'] ) && empty( $orderby ) ) {
$ids = implode(',', array_map('absint', $args['include']) );
$orderby = "FIELD( t.term_id, $ids )";
}
return $orderby;
}
add_filter( 'get_terms_orderby', 'w4dev_get_terms_orderby', 10, 2 );
I haven’t noticed any difference using any of these two hooks, none with performance or with compatibility.
Example Usage
$tags = get_terms( 'post_tag', array(
'include' => array( 21, 11, 31 ),
'orderby_include' => true,
'hide_empty' => false
));