• I am trying to randomly select a product variation from the dropdown on single product page in Woocommerce. I have created the following function for this using JavaScript:

    if($('select#account option').length > 0){
        var accsInStock = [];
    
        $('select#account option').each(function() {
            if( $(this).val().length > 0){
                accsInStock.push($(this).val());
                console.log("Account" + $(this) + " toegevoegd");
            }
        });
    
        const randomAcc  = Math.floor(Math.random() * accsInStock.length); 
    
        $('select#account option').each(function() {
            if($(this).val() == accsInStock[randomAcc] ){
                $(this).attr('selected', 'selected');
                console.log("Account " + accsInStock[randomAcc] + " geselecteerd");
                return;
            }
        });
    }

    The problem is that Woocommerce initially populates the select dropdown also with product variations that are not in stock. This causes my function to select a product variation sometimes that is not in stock.

    I have tracked down to Woocommerce template called variation.php, and saw that Woocommerce does not check if a product variation is available before populating the select dropdown with all the product variations.

    variable.php

    <?php
    
    defined( 'ABSPATH' ) || exit;
    
    global $product;
    
    $attribute_keys  = array_keys( $attributes );
    $variations_json = wp_json_encode( $available_variations );
    $variations_attr = function_exists( 'wc_esc_json' ) ? wc_esc_json( $variations_json ) : _wp_specialchars( $variations_json, ENT_QUOTES, 'UTF-8', true );
    
    do_action( 'woocommerce_before_add_to_cart_form' ); ?>
    
    <form class="variations_form cart" action="<?php echo esc_url( apply_filters( 'woocommerce_add_to_cart_form_action', $product->get_permalink() ) ); ?>" method="post" enctype='multipart/form-data' data-product_id="<?php echo absint( $product->get_id() ); ?>" data-product_variations="<?php echo $variations_attr; // WPCS: XSS ok. ?>">
        <?php do_action( 'woocommerce_before_variations_form' ); ?>
    
        <?php if ( empty( $available_variations ) && false !== $available_variations ) : ?>
            <p class="stock out-of-stock"><?php echo esc_html( apply_filters( 'woocommerce_out_of_stock_message', __( 'This product is currently out of stock and unavailable.', 'woocommerce' ) ) ); ?></p>
        <?php else : ?>
            <table class="variations" cellspacing="0">
                <tbody>
                    <?php foreach ( $attributes as $attribute_name => $options ) : ?>
                        <tr>
                            <td class="label"><label for="<?php echo esc_attr( sanitize_title( $attribute_name ) ); ?>"><?php echo wc_attribute_label( $attribute_name ); // WPCS: XSS ok. ?></label></td>
                            <td class="value">
                                <?php
                                    wc_dropdown_variation_attribute_options(
                                        array(
                                            'options'   => $options,
                                            'attribute' => $attribute_name,
                                            'product'   => $product,
                                        )
                                    );
                                    echo end( $attribute_keys ) === $attribute_name ? wp_kses_post( apply_filters( 'woocommerce_reset_variations_link', '<a class="reset_variations" href="#">' . esc_html__( 'Clear', 'woocommerce' ) . '</a>' ) ) : '';
                                ?>
                            </td>
                        </tr>
                    <?php endforeach; ?>
                </tbody>
            </table>

    I have been messing around myself a bit to see if I could make it work, but I failed. I was wondering if anyone would be able to help me to edit this file so the select dropdown only will be populated by product variations that are in stock. Any tips, help or feedback is much appreciated!

Viewing 6 replies - 1 through 6 (of 6 total)
  • Hi Steven! The out of stock variations will be hidden when the woocommerce_hide_out_of_stock_items option is set to true. So if you don’t mind that also being the case in the shop loop (hiding out of stock items) you could change the setting and that should (in theory) take care of it.

    Alternatively, you might need to filter that setting to be true somehow only in the context of getting the available variations.

    Another alternative, might be to filter woocommerce_hide_invisible_variations to true and then filter the variations to be not visible when out of stock.

    Option 1:

    
    function kia_hide_out_of_stock_variations() {
    	add_filter( 'option_woocommerce_hide_out_of_stock_items', 'kia_return_yes' );
    }
    add_action( 'woocommerce_variable_add_to_cart', 'kia_hide_out_of_stock_variations', 0 );
    
    function kia_return_yes() {
    	return 'yes';
    }
    
    function kia_restore_out_of_stock_variations() {
    	remove_filter( 'option_woocommerce_hide_out_of_stock_items', 'kia_return_yes' );
    }
    add_action( 'woocommerce_variable_add_to_cart', 'kia_restore_out_of_stock_variations', 99 );

    And Option 2:

    
    /**
     * Switch out of stock variations to not visible.
     *
     * @param  bool $is_visible
     * @param  int $variation_id
     * @param  int $parent_id - The ID of the variable product.
     * @param  WC_Product_Variation $variation
     *
     * @return bool
     */
    	
    function kia_out_of_stock_variations_not_visible( $is_visible, $variation_id, $parent_id, $variation ) {
    	return $variation->is_in_stock() ? $is_visible : false;
    }
    add_filter( 'woocommerce_variation_is_visible', 'kia_out_of_stock_variations_not_visible', 10, 4 );
    
    /**
     * Hide not-visible variations
     */
    add_filter( 'woocommerce_hide_invisible_variations', '__return_true' );

    `

    Both seem to work for me. Not sure there’s much difference. More than one way to cook shrimp!

    • This reply was modified 3 years, 8 months ago by HelgaTheViking.
    • This reply was modified 3 years, 8 months ago by HelgaTheViking. Reason: code formatting
    Thread Starter svw055

    (@svw055)

    @helgatheviking Thanks a lot for your reply I really appreciate it. I have tested both your solutions and they both do not work for me. All product variations are still initially loaded to the DOM, and then are hidden. Because of this, my function still first collects all the product variations and choose a random option out of the array, leading in out of stock variations being selected without being visible.

    Screenshot of while the page is loading (This is when my function chooses a random option from the select element. You can see both product variations are being loaded to the DOM, while one of them is out of stock.)

    View post on imgur.com

    Screenshot of when the page is done loading (only the in stock product variation is now visible)

    View post on imgur.com

    Hmm… ok, I see what you mean with respect to attributes being removed via scripts on the frontend when no variations match.

    Should note that there’s a delay for initializing the variations scripts (see add-to-cart-variation.js):

    
    // Init after gallery.
    setTimeout( function() {
    	$form.trigger( 'check_variations' );
    	$form.trigger( 'wc_variation_form', self );
    	self.loading = false;
    }, 100 );
    

    So if you are running your custom script first, Woo will not have been able to remove the attributes yet. I’m not 100% certain, but I think you could listen for the wc_variation_form trigger and try running your code then.

    The woocommerce_update_variation_values trigger (Source) might also be an option, but I think it will run every time the attributes are changed, which would not be ideal in your case, and you’d need to come up with a way to limit your script to running one time.

    Totally untested and just spitballing here… but perhaps if you wrap your code in the even I just mentioned, it may happen at a more appropriate time. Fingers crossed:

    
    jQuery( '.variations_form' ).on( 'wc_variation_form', function() {
      
        if($('select#account option').length > 0){
            var accsInStock = [];
    
            $('select#account option').each(function() {
                if( $(this).val().length > 0){
                    accsInStock.push($(this).val());
                    console.log("Account" + $(this) + " toegevoegd");
                }
            });
    
            const randomAcc  = Math.floor(Math.random() * accsInStock.length); 
    
            $('select#account option').each(function() {
                if($(this).val() == accsInStock[randomAcc] ){
                    $(this).attr('selected', 'selected');
                    console.log("Account " + accsInStock[randomAcc] + " geselecteerd");
                    return;
                }
            });
        }
      
    } );
    
    Thread Starter svw055

    (@svw055)

    @helgatheviking

    Dude you are an absolute hero. Thanks so much for diving into my problem, the code you provided me is working!

    I would like to send you a small tip because I appreciate it so much, I will send you a pm on slack

    Thread Starter svw055

    (@svw055)

    Actually it still doesn’t work as I would like to ?? because it now does select the correct product variation from the dropdown but it doesn’t seem to update the price of the selected product variation

Viewing 6 replies - 1 through 6 (of 6 total)
  • The topic ‘Only load product variations that are in stock’ is closed to new replies.