skip to Main Content

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!

2

Answers


  1. Read this first. Your $product has an is_type method and you can use it to check whether it’s variable. If so, you can get_available_variations, loop the result and apply your logic accordingly.

    if ( $product->is_type( 'variable' ) ) {
       $variations = $product->get_available_variations();
       foreach ( $variations as $variation ) {
           //Infer the size and the stock number from $variation and implement your logic
       }
    }
    

    You can also use get_children if that suits your needs better. So, you will need to revisit your show_attribute method, edit it, apply changes that reflect your logic regarding to product sizes and stock availability, using the resources described avoce.

    Login or Signup to reply.
  2. 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:

    • How it looks on the shop page : img1
    • Simple product is in stock : img2
    • Variable product that has in stock and out-of-stock variations : img3
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search