Count posts for each taxonomy term for each month

This is a long answer and there is a lot of code, please note that it is all not tested.

My solution use a function that create a multidimensional array where key are year, month, term id and the value are the term count.

The function also accept as argument an array of args to pass to WP_Query, limiting the term counting to desidered posts, so you can count terms of a taxonomy in a specific period using WP_Query time options or only for posts that respond to another taxonomy query using tax_query options, and so on.

Data ireturned by function are very rich and can be manipulated in a lot of ways, as example I will post a function that return the counting for a specific term in a specific month.

Anyway, here you are:

function set_cont_taxonomy_term_for_months($taxonomy = 'category', $query_args="")  {
  $defaults = array(
    'posts_per_page' => -1,
  );
  $args = wp_parse_args($query_args, $defaults);
  $query =  new WP_Query( $args );

  $output = array();

  if ( $query->have_posts() ) : while( $query->have_posts() ) :  $query->the_post();
    $terms =  wp_get_object_terms( get_the_ID(), $taxonomy, array('fields' => 'ids') );
    if ( is_array($terms) && ! empty($terms) ) {
      global $post;
      $year = (int) substr($post->post_date, 4, 0);
      $month = (int) substr($post->post_date, 5, 2);
      if ( ! isset($output[$year]) ) $output[$year] =  array();
      if ( ! isset($output[$year][$month]) ) $output[$year][$month] = array();
      foreach ( $terms as $term ) {
        $output[$year][$month][$term] = isset($output[$year][$month][$term]) ? $output[$year][$month][$term] + 1 : 1;
      }
    }
  endwhile;
  endif;
  wp_reset_postdata();
  return $output;
}

The output of this function is an array which an inner item is something like:

$output[2013][7][132] = 5

that is: in the July 2013 the term with ID 132 was used 5 times

Note that this assertion is valid ‘in general’ (for all posts) only if second argument of function is empty (as is by default) otherwise is valid only for posts responding to the query args array passed as second param to function.

Now, you can use the array generated by this function with this other one:

function get_month_term_count($term_id = 0,  $year="", $month="", $taxonomy = 'category', $query_args = array() ) {
   if ( ! $term_id ) return 0;
   if ( ! $year ) $year = (int)date('Y');
   if ( ! $month ) $year = (int)date('n');
   $count = (array) set_cont_taxonomy_term_for_months( $taxonomy, $query_args );
   return ( empty($count) || ! isset($count[$year][$month][$termid]) ) ? 0 : $count[$year][$month][$termid];
}

The output of this function is a number, that is the counting for a given term id in a given month for a given query posts.

Use it is a lot easier than explain:

Following code shows how many times category with id ‘3’ is used in posts of current month:

$cat = get_term(3);
$today = getdate();
$m = ($today["year"] * 100 ) + $today["mon"];
$count = get_month_term_count( 3, $today['year'], $today['mon'], 'category', array('m' =>$m) );
sprintf( __('In current month category %s is used %d times.'), $cat->name, $count);

As I previous said, the data generated by the set_cont_taxonomy_term_for_months are very rich, as example, following code shows tag counting in all the months of current year:

$today = getdate();
$count = set_cont_taxonomy_term_for_months( 'post_tag', array('year' => $today["year"] ) );

if ( ! empty($count) ) {
  foreach ( $count as $year => $year_array ) {
    echo '<h1>' __('Year: ') . $year '</h1>';
    foreach ( $year_array as $month => $month_array ) {
      echo '<h2>';
      _e('Month ') . date_i18n( "F", mktime(0, 0, 0, $month, 1, $year ) );
      echo '</h2>';
      echo '<ul>'; 
      foreach ( $month_array as $tag_id => $count ) {
        $tag = get_term($tag_id);
        if ( ! is_wp_error($tag) && ! empty($tag) ) {
           printf( __('<li>Tag %s used %d times</li>'), $tag->name, $count );
        }
      }
      echo '</ul>';
    }
  }
}

If you have a lot of posts, doing 3 nested foreach cycles will slow down the script, so try to use the function set_cont_taxonomy_term_for_months passing $query_args as selective as possible and, if you can, write functions to return desired informations without perform cycles, just like I do with with get_month_term_count function.

Hope it helps.

Edit:

I have changed the 3 nested loops in my last example, now it should works.

Leave a Comment

tech