I've been successfully using the following code from @StephenHarris from this answer. I have made a small tweak or two to the original code, but the most significant is to name the new count
object to count_type
from the original COUNT*
that was returned by default
Just in short again, the function works exactly like you would normally use get_terms
. There is an extra argument however, called post_type
with will accept an array or string of post type names. When the post_type
arguments is set, the function uses the terms_clauses
filter to get the terms by that specific post type only
NOTE: One big caution when setting a post type that was not mentioned previously, the function returns two count objects with keys count
and (now named from COUNT*
) count_type
. count
still retrieves the post count for the specific term across the board, not the count for the specific post type. To display the correct count for the post type given, you will have to make use of count_type
which will display the correct count
As example, here is the returned object when a post type is set
object(stdClass)#583 (10) {
["term_id"]=>
string(3) "145"
["name"]=>
string(7) "testing"
["slug"]=>
string(7) "testing"
["term_group"]=>
string(1) "0"
["term_taxonomy_id"]=>
string(3) "145"
["taxonomy"]=>
string(8) "post_tag"
["description"]=>
string(0) ""
["parent"]=>
string(1) "0"
["count"]=>
string(1) "4"
["count_type"]=>
string(1) "2"
}
You can see count
is 4
, this is the total posts from all post types, count_type
is 2
, this is the amount of posts with the specific tag in the specified post type
Here is the code that goes into your functions.php (Again, special thanks to @StephenHarris for the original code)
function get_terms_per_post_type( $taxonomies, $args=array() ) {
//Parse $args in case its a query string.
$args = wp_parse_args($args);
if( !empty( $args['post_type'] ) ){
$args['post_type'] = (array)$args['post_type'];
add_filter( 'terms_clauses', function ( $pieces, $tax, $args){
global $wpdb;
//Don't use db count
$pieces['fields'] .= ", COUNT(*) AS count_type" ;
//Join extra tables to restrict by post type.
$pieces['join'] .= " INNER JOIN $wpdb->term_relationships AS r ON r.term_taxonomy_id = tt.term_taxonomy_id
INNER JOIN $wpdb->posts AS p ON p.ID = r.object_id ";
//Restrict by post type and Group by term_id for COUNTing.
$post_type = implode( ',', $args['post_type'] );
$pieces['where'] .= $wpdb->prepare( " AND p.post_type IN(%s) GROUP BY t.term_id", $post_type );
remove_filter( current_filter(), __FUNCTION__ );
return $pieces;
}, 10, 3 );
}
return get_terms($taxonomies, $args);
}
You can use it as follows in your template files
$terms = get_terms_per_post_type( 'post_tag', array( 'post_type' => 'post' ) );
foreach ( $terms as $term ) {
echo "$term->name ( $term->count_type ) </br>";
}