Show certain terms from custom taxonomy but exclude ‘parent’ terms?

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().

Leave a Comment