3

I will try to be as explicit and short as possible. Suppose I have a site for books. I have three categories named adventure, romance and fantasy. Then, I have a tag called old books. In the top of this tag archive I need to display some counts information. Example :

OLD BOOKS

There are a total of 88 old books available.
   18 books are from adventure category.
   50 books are from romance category.
   20 books are from fantasy category.

I solved the first one with $wp_query->found_posts; which gives me the count of total number of books in the tag-archive. Now how do I get the count for rest of the books. A solution or a road map would be great otherwise please tell me what functions or example should I be looking into.

I also expect the count to change dynamically as the changes occur. I will be posting my updates shortly as I am struggling for solution.

5
  • 1
    How many books will there actually be? There is choice here roughly between straightforward querying all posts approach (which would be easy, but slow) and convoluted SQL approach (which will be fast, but nightmare to implement). – Rarst Apr 6 '14 at 12:16
  • Although, I did not catch the context of your question. What I know is there may be thousands of books on the site under several other categories but under this specific tag archive it only has 88 books. I didnt know this would be so twisty. :( – gurung Apr 6 '14 at 12:24
  • 1
    For reasonably low number — query all posts, get their categories and add up. It will however choke for larger numbers. – Rarst Apr 6 '14 at 12:26
  • How many categories? – s_ha_dum Apr 6 '14 at 16:18
  • @s_ha_dum maximum to about 50 may be in future, currently about 20 – gurung Apr 6 '14 at 16:20
2

I remembered that once I had to do same thing and wrote a custom sql query... it was painful, as @Rarst said.

However I'll post the function I wrote, and there isn't much to explain, is a complex SQL query where there is the intersection of 2 checks to get all the term of a specific taxonomy that are associated in post having a specific term in another taxonomy:

function tax_mix_count( $term, $tax, $to_count, $post_type = '' ) {
  if ( ! did_action( 'wp_loaded' ) && current_filter() !== 'wp_loaded' ) {
    return new WP_Error('too-early', 'Call tax_mix_count() after wp_loaded');
  }
  if ( ! taxonomy_exists( $tax ) || ! taxonomy_exists( $to_count ) ) {
    return new WP_Error('invalid-tax', 'Pass valid taxonomy names to tax_mix_count()');
  }
  $by = is_numeric( $term ) ? 'term_id' : 'slug';
  $term_obj = get_term_by( $by, $term, $tax );
  if ( empty( $term_obj ) || is_wp_error( $term_obj ) ) {
    return new WP_Error( "invalid-term", "Pass valid {$tax} term to tax_mix_count()" );
  }
  global $wpdb;
  $pt_where = '';
  if ( is_string( $post_type ) && post_type_exists( $post_type )  ) {
    $pt_where = $wpdb->prepare(' AND p.post_type = %s', $post_type );
  }
  $query = "
    SELECT t.term_id FROM {$wpdb->term_taxonomy} tt
    INNER JOIN {$wpdb->terms} t ON tt.term_id = t.term_id
    INNER JOIN {$wpdb->term_relationships} tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
    INNER JOIN {$wpdb->posts} p ON (p.ID = tr.object_id) 
    INNER JOIN {$wpdb->term_relationships} tr2 ON (p.ID = tr2.object_id)
    INNER JOIN {$wpdb->term_taxonomy} tt2 ON (tt2.term_taxonomy_id = tr2.term_taxonomy_id) 
    WHERE tt.taxonomy = %s
      AND p.post_status = 'publish'{$pt_where}
      AND tt2.taxonomy = %s AND tt2.term_id = %d";
  $c = $wpdb->get_col( $wpdb->prepare( $query, $to_count, $tax, $term_obj->term_id ) );
  if ( empty( $c ) ) return false;
  return array_count_values( $c );
}

As you can see it's possible to use with different taxonomies and not only tags and categories.

  • The first argument, $term can be a term id or a term slug.
  • The second argument is the taxonomy the term belong.
  • The third argument is the taxonomy to count.
  • The fourth argument (optional) is the post type to limit the count.

Is possible do not to pass any post type as well if the two taxonomies are used by only one custom post type or if one want ot count all post types.

Note that if an invalid CPT is passed it is skipped without return any error.

The function also check if the hook 'wp_loaded' was already fired, because before that taxonomies and post types are not registered, so checks for taxonomies or post type existence will fail.

How to use:

$category_old_books = tax_mix_count( 'old', 'post_tag', 'category', 'books' );

if ( $category_old_books === FALSE ) {
  '<p>Posts in "old" tag are not associated with categories.</p>';
} elseif ( is_array( $category_old_books ) ) {
  foreach( $category_old_books as $id => $count ) {
    $term = get_term( $id, 'category' );
    $url = add_query_arg( array('tag'=>'old'), get_term_link($term,'category') );
    printf(
     _n(
       '%d book is from <a href="%s">%s</a> category',
       '%d books are from <a href="%s">%s</a> category.',
       $count,
       'your_textdomain'
      ),
      $count, esc_url( $url ), esc_html( $term->name )
    );
    echo '<br>';
  }
}

Of course replace 'your_textdomain' with your real text domain ;)

3
  • works very nicely. What will change if I need the links to categories instead of plain text ? – gurung Apr 7 '14 at 5:56
  • @gurung updated the answer (both code snippets) to show links instead of plain names. Note that the link ppoint to the category archive showing only the book having category and tag 'old'. To show the full category archive, replace the line $url = add_query_arg( array('tag'=>'old'), get_term_link($term,'category') ); with $url = get_term_link($term,'category'); – gmazzap Apr 7 '14 at 10:29
  • 1
    this more than perfect. The archive links with ?tag=old is actually helping me more to sort books I did not know were having wrong tags. Thanks for sharing your wisdom. – gurung Apr 7 '14 at 11:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.