admin管理员组文章数量:1321615
I need to list all custom terms (brands) that are associated with products from the category that is currently being viewed. E.g. I have these brands created:
- Shirt Brand A
- Shirt Brand B
- Shirt Brand C
- Jeans Brand A
- Jeans Brand B
On 'Shirts' category page, only Shirts Brands A
, B
and C
are displayed.
This is how I did it:
$args = array(
'taxonomy' => 'brand',
'orderby' => 'count',
'order' => 'DESC',
'hide_empty' => false
);
$brands = get_terms($args);
foreach($brands as $brand) {
if(is_product_category()) {
$cat_id = get_queried_object_id();
$count_args = array(
'post_type' => 'product',
'post_status' => 'publish',
'tax_query' => array(
array(
'taxonomy' => 'product_cat',
'field' => 'term_id',
'terms' => $cat_id,
'operator' => 'IN'
),
array(
'taxonomy' => 'brand',
'field' => 'slug',
'terms' => $brand->slug,
'operator' => 'IN'
)
)
);
$count_products = new WP_Query($count_args);
if($count_products->post_count <= 0) continue; // Skip if this query contains zero products
// Display brand that has at least 1 product in this category
display_brand();
}
}
Of course it works, however with 300+ brands created it sometimes takes 0,5 - 1,5 seconds for this loop to get processed, which is unnecessary lot. AFAIK there is no direct database relation between terms and product categories (only number of posts for each term), but is there a better way to do this with better performance?
I need to list all custom terms (brands) that are associated with products from the category that is currently being viewed. E.g. I have these brands created:
- Shirt Brand A
- Shirt Brand B
- Shirt Brand C
- Jeans Brand A
- Jeans Brand B
On 'Shirts' category page, only Shirts Brands A
, B
and C
are displayed.
This is how I did it:
$args = array(
'taxonomy' => 'brand',
'orderby' => 'count',
'order' => 'DESC',
'hide_empty' => false
);
$brands = get_terms($args);
foreach($brands as $brand) {
if(is_product_category()) {
$cat_id = get_queried_object_id();
$count_args = array(
'post_type' => 'product',
'post_status' => 'publish',
'tax_query' => array(
array(
'taxonomy' => 'product_cat',
'field' => 'term_id',
'terms' => $cat_id,
'operator' => 'IN'
),
array(
'taxonomy' => 'brand',
'field' => 'slug',
'terms' => $brand->slug,
'operator' => 'IN'
)
)
);
$count_products = new WP_Query($count_args);
if($count_products->post_count <= 0) continue; // Skip if this query contains zero products
// Display brand that has at least 1 product in this category
display_brand();
}
}
Of course it works, however with 300+ brands created it sometimes takes 0,5 - 1,5 seconds for this loop to get processed, which is unnecessary lot. AFAIK there is no direct database relation between terms and product categories (only number of posts for each term), but is there a better way to do this with better performance?
Share Improve this question asked Sep 16, 2020 at 13:53 Kristián FiloKristián Filo 4316 silver badges20 bronze badges2 Answers
Reset to default 3To 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.
Define your terms as hierarchical custom taxonomies, as child taxonomies from the corresponding parent taxonomy you want them to inherit from...? Like this you would only need to query for your parent tax, and simply echo out the respective child taxonomies, if required.
** UPDATE **
What you do is query all the terms of a given taxonomy, and use them to query posts having them, so you're creating a WP_Query object for every single brand tax, which is unnecessarily expensive. You should do it the other way around; simply query posts which have terms of the brand taxonomy associated to it (and whichever additional feature you may want to use for the query) ONCE only, and while retrieving these posts (inside your wp_query loop), access the id of the currently queried post in the loop, and retrieve the brand
terms associated to it. Sth like this:
$product_query_args = array(
'post_type' => 'your_post_type',
'tax_query' => array(
array(
'taxonomy' => 'brand'
)
)
);
$products_request = new WP_Query( $product_query_args );
if ( $products_request->have_posts() ) {
while ( $products_request->have_posts() ) {
$products_request->the_post();
// Get ID of currently iterated post
$id_of_iterated_product = get_the_ID();
// Retrieve associated brand terms
$brands = get_the_terms( $id_of_iterated_product, 'brand' );
// Check if there are brand terms associated to iterated post
if ( ! empty( $brands ) ) {
// If so, iterate through them and get the name of each
foreach( $brands as $brand_key => $brand_object ) {
$brand_name = $brand_object->name;
// Do whatever you want with it
}
}
}
}
Like this, you actually only make one full query instead of countless, so performance should be better, hopefully. Let me know how this solution suits you.
To explain it a little further to you why I thought of hierarchical taxonomies; you wrote:
"I need to list all custom terms (brands) that are associated with products from the category that is currently being viewed."
- From this I understand that there's something like a category filter on your website, and you wanna display all the posts of that category, and then echo out the brands for each of these posts. For this, what you could actually do is simply query posts in function of the selected category (makes your query a lot easier), and then for each post retrieve and spit out the associated brands (which are all child tax's of the queried category, if you defined them to be so). You can however reach the same in querying your products in function of the given category, and then retrieve all the brand tags associated to each retrieved post as I did in my code; maybe that's even better when it comes to performance. But from a logical point of view, using taxonomy hierarchics and one single category query appears to be the easiest and most logical way of doing this.
本文标签: categoriesGet terms that are associated with products from current category
版权声明:本文标题:categories - Get terms that are associated with products from current category 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742103764a2420925.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论