In an online store on WooCommerce, I use code that displays certain product attributes on archive/category pages.
add_action( 'woocommerce_before_shop_loop_item_title', 'new_template_loop_product_meta', 20 );
function new_template_loop_product_meta() {
global $product;
$attrs_by_cats = [
20 => [ 'pa_size' ],
];
$attr_list = [
'Size' => 'pa_size',
];
if ( ! is_object( $product ) ) {
$product = wc_get_product( get_the_id() );
}
$cats = $product->get_category_ids();
if ( ! is_array( $cats ) ) {
return;
}
$attrs = [];
foreach ( $cats as $cat ) {
if ( isset( $attrs_by_cats[ $cat ] ) ) {
$attrs[] = $attrs_by_cats[ $cat ];
}
}
$allowed_attrs = array_unique( array_merge( [], ...$attrs ) );
echo '<div class="custom-attributes">';
foreach ( $attr_list as $attr_title => $attr_name ) {
if ( in_array( $attr_name, $allowed_attrs, true ) ) {
show_attribute( $product, $attr_title, $attr_name );
}
}
echo '</div>';
}
/* Show attr */
function show_attribute( $product, $attr_title, $attr_name ) {
if ( 'sku' === $attr_name ) {
$attr = (string) esc_html( $product->get_sku() );
} else {
$attr = $product->get_attribute( $attr_name );
if ( ! $attr ) {
return;
}
$attr = explode( ', ', $attr ); // convert the coma separated string to an array
$attr_arr = []; // Initialize
// Loop through the term names
foreach ( $attr as $term_name ) {
// Embed each term in a span tag
$attr_arr[] = sprintf('<span class="attr-term">%s</span>', $term_name);
}
// Convert back the array of formatted html term names to a string
$attr = implode(' ', $attr_arr);
}
if ( '' === $attr ) {
return;
}
printf( '<div class="custom-attributes-text">%s: %s</div>', $attr_title, $attr);
}
With simple products this code works without problems. The problem is only with the attribute in variable products.
When creating a product I added sizes S, M, L and automatically created variations. Then for each size S, M and L I manually set the stock availability to 30.
Then, the size L is all sold out and I have 0 in stock. Except that on the product list page all sizes are shown, while S and M should be shown.
How can I fix this code so that it works with variable products?
Thanks in advance for your help!
Conditions that check the stock status in your show_attribute
function were missing, you can add them by following below code, replace your show_attribute
function with this.
function show_attribute( $product, $attr_title, $attr_name ) {
if ( 'sku' === $attr_name ) {
$attr = (string) esc_html( $product->get_sku() );
} else {
// ===============>
$stock_status = $product->get_stock_status();
if ( $stock_status === 'outofstock' ) {
return; // do not show size attributes if simple product is out of stock
}
// <===============
$attr = $product->get_attribute( $attr_name );
if ( ! $attr ) {
return;
}
$attr = explode( ', ', $attr ); // convert the coma separated string to an array
// ===============>
$in_stock_attr = $attr;
if ( $product->is_type( 'variable' ) ) {
$product = new WC_Product_Variable( $product );
$variations = $product->get_available_variations();
$variations_ids = wp_list_pluck( $variations, 'variation_id' );
if ( is_array( $variations_ids ) && ! empty( $variations_ids ) ) {
foreach ( $variations_ids as $variation_id ) {
$variation = new WC_Product_Variation( $variation_id );
$stock_status = $variation->get_stock_status();
if ( $stock_status === 'outofstock' ) {
$variation_attr = $variation->get_attribute( $attr_name );
if ( ( $key = array_search( $variation_attr, $in_stock_attr ) ) !== false ) {
unset( $in_stock_attr[ $key ] ); // Do not pick out of stock attribute for display
}
}
}
}
}
$attr = $in_stock_attr;
// <===============
$attr_arr = []; // Initialize
// Loop through the term names
foreach ( $attr as $term_name ) {
// Embed each term in a span tag
$attr_arr[] = sprintf( '<span class="attr-term">%s</span>', $term_name );
}
// Convert back the array of formatted html term names to a string
$attr = implode( ' ', $attr_arr );
}
if ( '' === $attr ) {
return;
}
printf( '<div class="custom-attributes-text">%s: %s</div>', $attr_title, $attr );
}
Images to show how it works on my side: