Count posts in category including child categories

All we need is an easy explanation of the problem, so here it is.

I have found this code somewhere, it counts posts but only which are under specific category. I would appreciate if someone could expand this so it would count all posts which also belong to its child categories?

function wp_get_cat_postcount($id) {

      $cat =  get_category_by_slug( $id ); 
      $count = (int)$cat->count;
      $taxonomy = 'category';
      $args = array(
        'child_of' => $id,
        );
      $tax_terms = get_terms($taxonomy,$args);
      foreach ($tax_terms as $tax_term) {
        $count +=$tax_term->count;
      }
      return $count;
    }

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

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 matching 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 what will be happening

/**
 * Function 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;

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply