Count posts in category including child categories

The function you are using are scrambled and does not make much sense. Also, it is very static in that you cannot return a count according to custom parameters like post type, post status, or custom field. Another thing to note, if a post belongs to a term and one of its child terms, the post will be counted twice, so you will end up with a post count which is more than what you actually have.

IMHO, I will just make use of a custom query to make the function dynamic and to return a true count instead of a bloated one due to posts belonging to a parent and a child term.

THE IDEA

The tax_query has a parameter called include_children, which by default is set to true. This parameter will include all child terms of the terms passed to the terms parameter, so it will return posts from the passed terms and all of it’s children.

Secondly, we only need to query one post to get a post count. What WP_Query does by default is, it will continue to search the db for matching posts, even though it has already found the required posts mathcing the query. This is done in order to calculate pagination, and a count of all these posts matching the query is stored in the $found_posts property of the query object.

To save on db calls and time spend doing it, we will also just query the ID post field and not the complete WP_Post object.

THE CODE

First of all, a few notes

  • The code is untested and might be buggy. Be sure to test this first on a local test install with debug turned on

  • The code requires a minimum of PHP 5.4

I will comment the code as I go along so you can follow and understand wht will be happening

/**
 * Funtion to get post count from given term or terms and its/their children
 *
 * @param (string) $taxonomy
 * @param (int|array|string) $term Single integer value, or array of integers or "all"
 * @param (array) $args Array of arguments to pass to WP_Query
 * @return $q->found_posts
 *
 */
function get_term_post_count( $taxonomy = 'category', $term = '', $args = [] )
{
    // Lets first validate and sanitize our parameters, on failure, just return false
    if ( !$term )
        return false;

    if ( $term !== 'all' ) {
        if ( !is_array( $term ) ) {
            $term = filter_var(       $term, FILTER_VALIDATE_INT );
        } else {
            $term = filter_var_array( $term, FILTER_VALIDATE_INT );
        }
    }

    if ( $taxonomy !== 'category' ) {
        $taxonomy = filter_var( $taxonomy, FILTER_SANITIZE_STRING );
        if ( !taxonomy_exists( $taxonomy ) )
            return false;
    }

    if ( $args ) {
        if ( !is_array ) 
            return false;
    }

    // Now that we have come this far, lets continue and wrap it up
    // Set our default args
    $defaults = [
        'posts_per_page' => 1,
        'fields'         => 'ids'
    ];

    if ( $term !== 'all' ) {
        $defaults['tax_query'] = [
            [
                'taxonomy' => $taxonomy,
                'terms'    => $term
            ]
        ];
    }
    $combined_args = wp_parse_args( $args, $defaults );
    $q = new WP_Query( $combined_args );

    // Return the post count
    return $q->found_posts;
}

USAGE

You can use the function in the following ways

CASE 1

Single term (term ID 21) with default category taxonomy

$count = get_term_post_count( 'category', 21 );
echo $count;

CASE 2

Array of term ids with custom taxonomy my_taxonomy

$count = get_term_post_count( 'my_taxonomy', [21, 41, 52] );
echo $count;

CASE 3

Single term from default category taxonomy from custom post type cpt and post status trash

$args = [
    'post_type'   => 'cpt',
    'post_status' => 'trash'
];
$count = get_term_post_count( 'category', 21, $args );
echo $count;

USAGE 4

If you need to get a post count from all terms of a given taxonomy, simply set the $term parameter to all

$count = get_term_post_count( 'category', 'all' );
echo $count;

Leave a Comment