Get terms that are associated with products from current category

To fine-tune DevelJoe’s answer a bit more, you could access the queried posts from the global $wp_query instead of doing an extra WP_Query.

// make the current query available from global variable
global $wp_query;
// helper variable
$brands = array();
// loop queried posts
foreach ( $wp_query->posts as $queried_post ) {
    // check if they have terms from custom taxonomy
    $current_post_brands = get_the_terms( $queried_post, 'brands' ); // post object accepted here also, not just ID
    if ( ! is_wp_error( $current_post_brands ) && $current_post_brands ) {
        // push found brands to helper variable
        foreach ( $current_post_brands as $current_post_brand ) {
            // avoid having same brands multiple time in the helper variable
            if ( ! isset( $brands[$current_post_brand->term_id] ) ) {
                $brands[$current_post_brand->term_id] = $current_post_brand;
            }
        }
    }
}

// do something with the brand terms (WP_Term)
foreach ( $brands as $brand_id => $brand_term ) {
    // yay?
}

This is probably a faster way, because WP automatically caches the terms of the queried posts – to my undestanding. This caching is dicussed for example here, Explanation of update_post_(meta/term)_cache


EDIT 23.9.2020

(Facepalm) Let me try this again…

One way could be to first query all the posts in the current category. Then loop found posts to check their brand terms. Finally spit out an array of unique brand terms. Optionally you could also save the result into a transient so that the querying and looping doesn’t run on every page load, thus saving some server resources.

function helper_get_brands_in_category( int $cat_id ) {
    $transient = get_transient( 'brands_in_category_' . $cat_id );
    if ( $transient ) {
        return $transient;
    }
    
    $args = array(
        'post_type' => 'product',
        'posts_per_page' => 1000, // is this enough?
        'post_status' => 'publish',
        'no_found_rows' => true,
        'update_post_meta_cache' => false, // not needed in this case
        'fields' => 'ids', // not all post data needed here
        'tax_query' => array(
            array(
                'taxonomy' => 'product_cat',
                'field' => 'term_id',
                'terms' => $cat_id,
            )
        ),
    );
    $query = new WP_Query( $args );
    $brands = array();
    
    foreach ($query->posts as $post_id) {
        $post_brands = get_the_terms( $post_id, 'brands' );
        if ( ! is_wp_error( $post_brands ) && $post_brands ) {
            foreach ( $post_brands as $post_brand ) {
                if ( ! isset( $brands[$post_brand->term_id] ) ) {
                    $brands[$post_brand->term_id] = $post_brand;
                }
            }
        }
    }

    set_transient( 'brands_in_category_' . $cat_id, $brands, WEEK_IN_SECONDS ); // change expiration date as needed

    return $brands;
}
// Usage
$brands_in_category = helper_get_brands_in_category( get_queried_object_id() );

If you save the brands into transients, then you may also want to invalidate the transients whenever new brands are added. Something along these lines,

function clear_brands_in_category_transients( $term_id, $tt_id ) {
    $product_cats = get_terms( array(
        'taxonomy' => 'product_cat',
        'hide_empty' => false,
        'fields' => 'ids',
    ) );
    if ( ! is_wp_error( $product_cats ) && $product_cats ) {
        foreach ($product_cats as $cat_id) {
            delete_transient( 'brands_in_category_' . $cat_id );
        }
    }
}
add_action( 'create_brands', 'clear_brands_in_category_transients', 10, 2 );

I used the create_{$taxonomy} hook above.

Leave a Comment