phpwordpresswoocommerceproduct

Attribute values in variable products on shop/categories WooCommerce pages


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!


Solution

  • 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: