I have a custom post type "mycustom_products" (hierarchical) that has custom taxonomies called 'categories' and 'tags' (as many other custom post types do). My custom category is called "myprod_category" (also hierarchical) and my tag taxonomy is called "product_tag" (not hierarchical).

I want to show a list of Product Tags that correspond ONLY to certain Product Categories.

For example, with Product Categories like "soap" & "lotion", there might be tags called "Blueberry" or "Lavender" (scents) but "jewelry" would have tags like "silver" or "gold". I want to be able to have a listing of the "scent" only tags (which would be the ones that correspond only with the "soap" and "lotion" categories. I don't want the tags for "jewelry" to be included. So any tags that have been used on products entered in those categories would be included, and any used for other categories wouldn't be.

Obviously I could hard code everything into the theme, but if/when the client adds more tags, I would have to go in and re-code everything and I'd like to avoid doing that.

I tried using <?php wp_tag_cloud( array( 'taxonomy' => 'product_tag', format => 'list' ) ); ?> to list out all my tags, but I can't seem to get the EXCLUDE argument to prevent whole "product categories" from showing. Seems like I can do it using the tag_ID but that would be tedious, and would put me back to hard coding them into the theme and having to update each time a new tag is added.

Is there a filter or something I can use to exclude a category? Or is there a better way to list tags in this fashion. Thanks!

  • @RodeoRamsey: Your question is a bit confusing because you use the words "tag", "category", "taxonomy" (almost) interchangeably. "Categories" and "Tags" are taxonomies, and you can add extra custom taxonomies, like you did (named product_tag). Taxonomies can be hierarchical (like "Category") or non-hierarchical (like "Tag"). I think yours is hierarchical? Individual words in an taxonomy are called terms. So (I think) you mean "How can I show terms from a custom taxonomy while excluding terms from a 'parent' term?" Can you rewrite your question to make this more clear?
    – Jan Fabry
    May 5, 2011 at 7:14
  • Hi Jan, sorry for the confusion! Yes I believe your suggested question is more what I was trying to say. LOL. It was late and the whole process is confusing to me. I'll try and rewrite the question to be clearer. Thanks. May 5, 2011 at 12:48
  • The issue is that there's an implied relationship between your two separate taxonomies, but nothing explicit beyond the loose relationship of both being assigned to the same post.
    – Milo
    May 8, 2011 at 15:13
  • Did you try? It worked?
    – MZAweb
    May 13, 2011 at 23:32
  • I have to ask, why no use sub categories instead of tags?
    – Bainternet
    May 14, 2011 at 23:45

2 Answers 2


If you have a lot of products, probably a custom sql query is a better option.

I'd probably recommend following that route either way. Custom queries may be 'icky', but it'll lighten resources and be a darn slight quicker in most cases*

I've written a function that does most of the labour, and may well come in handy in other scenarios (I've used the word 'soft' to imply the relations are not explicit).

 * Get terms that are indirectly associated with others through the posts they
 * are attached to.
 * @see http://codex.wordpress.org/Function_Reference/WP_Query#Taxonomy_Parameters
 * @link http://wordpress.stackexchange.com/questions/16393/
 * @param string|array $taxonomy The taxonomy(s) of the terms you wish to obtain.
 * @param array $tax_query A tax query for the posts you want to make the association with.
 * @return array Array of term IDs.
function get_terms_soft_associated( $taxonomy, $tax_query  )
    global $wpdb;

    // so you can pass a single tax query rather than wasted nested array
    if ( isset( $tax_query['taxonomy'] ) )
        $tax_query = array( $tax_query );

    $tax = new WP_Tax_Query( $tax_query );
    extract( $tax->get_sql( $wpdb->posts, 'ID' ) );

    if ( empty( $join ) || ( !$posts = $wpdb->get_col( "SELECT $wpdb->posts.ID FROM $wpdb->posts $join WHERE 1=1 $where" ) ) )
        return array();

    $taxonomy = implode( "','", array_map( 'esc_sql', ( array ) $taxonomy ) );
    $posts = implode( ',', wp_parse_id_list( $posts ) );

    return $wpdb->get_col(
        "SELECT DISTINCT t.term_id FROM $wpdb->terms AS t " .
        "INNER JOIN $wpdb->term_taxonomy AS tt ON tt.term_id = t.term_id " .
        "INNER JOIN $wpdb->term_relationships AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id " .
        "WHERE tt.taxonomy IN('$taxonomy') AND tr.object_id IN($posts)"

You can also check out the codex on tax queries for more help with $tax_query. This is the query we're using to filter posts, before we do a reverse lookup to gather all their terms for another taxonomy.

Now onwards to the solution;

// suggestion 1 - for a single post
$product_cats = get_the_terms( get_the_ID(), 'myprod_category' );

// suggestion 2 - for a product category archive
$product_cats = get_queried_object_id();

// suggestion 3 - for all posts on the current page of an archive
foreach( $wp_query->posts as $_post ) {
    $_product_cats = get_the_terms( $_post->ID, 'myprod_category' );
    foreach ( $_product_cats as $_product_cat )
        $product_cats[] = $_product_cat->term_id;

// now get the 'associated' tag IDs
$product_assoc_tags = get_term_soft_association( 'product_tag', array(
    'taxonomy' => 'myprod_category',
    'field'    => 'term_id',
    'terms'    => $product_cats
) );

wp_tag_cloud( array( 'taxonomy' => 'product_tag', 'include' => $product_assoc_tags ) );

Note the suggestions are examples of how to grab all the product categories to later query against, depending on your circumstance and which post(s) you want to affect the outcome (only use one of the suggestions, or your own!).

If you find it's not working as you expected, chances are we just need to tweak the tax query for the second argument of the function.

*Footnote: Native post querying pulls in all post data, whilst we only need to work with IDs. Saving memory where you can always helps, and as @Daniel says, if we're talking a lot of posts, we're also talking a lot of memory.

I also say quicker as, under out-of-the-box conditions, we're using far fewer database queries & processing than had we used functions like get_terms() and get_posts().

  • Very nice! Cool function to have in the arsenal
    – MZAweb
    May 9, 2011 at 21:52

If I understand you correctly, I think there is no direct option.

You should query all products within the category you want, iterate those records, and get the tags for each product. This will be SLOW if you have a lot of products.

Something like (this is for regular categories / tags, you should modify it to your custom taxonomies)

    $posts = get_posts("cat=5&numberposts=-1");
$tags = Array();
foreach ($posts as $ipost){
    $posttags = get_the_tags($ipost->ID);
    foreach ($posttags as $posttag){
        if (!in_array($posttag->term_id, $tags)){
            array_push($tags, $posttag->term_id);


Now you have in $tags the unique IDs of the tags you're looking for.

If you have a lot of products, probably a custom sql query is a better option.

  • The TheDeadMedic's approach should perform (a lot) better in almost every case. Anyway, if you test both, please tell us the results.
    – MZAweb
    May 9, 2011 at 23:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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