Categories, Tags, Terms Custom Order

WordPress doesn’t order categories or terms by the ids you provide out of the box. You have probably seen, while doing a post query ( get_posts(), WP_Query() ), if we set the post__in argument with an array of post ids, the query orders the results exactly by the given ids. But when using get_terms(), if we pass the include argument, the results don’t come back in the order the term ids were passed. So I figured out a solution digging the core code in the file wp-includes/taxonomy.php.

Update: since WordPress 4.7, core supports this natively. You can simply pass 'orderby' => 'include' to get_terms() and skip the custom hooks entirely:

$tags = get_terms( array(
    'taxonomy'   => 'post_tag',
    'include'    => array( 21, 11, 31 ),
    'orderby'    => 'include',
    'hide_empty' => false
));

If you need a different custom order, or you want to understand how the ordering can be filtered, we can use either of two available hooks, get_terms_orderby or terms_clauses.

Terms Order by provided ids using terms_clauses hook

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

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


Terms Order by provided ids using get_terms_orderby hook

Processing with this hook is the same as the previous one. But here I tried to use a fixed condition – “If the include argument is defined, and orderby is left empty, we will order by the included 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 between these two hooks, neither with performance nor with compatibility.

Example Usage

$tags = get_terms( array(
    'taxonomy'        => 'post_tag',
    'include'         => array( 21, 11, 31 ),
    'orderby_include' => true,
    'hide_empty'      => false
));

Leave a Reply

Your email address will not be published. Required fields are marked *