Display tags belonging to a specific post type only

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>";
}

Leave a Comment