Skip to content
Advertisement

Sync two product quantities at cart woocommerce

I am trying to sync two product ids at the cart in woocommerce so that change in one bring change in the other quantity, I am trying this code but it updates all the products in the cart with master product ID, which I dont want.

add_action( 'template_redirect', 'bbloomer_sync_cart_quantities' );
    
function bbloomer_sync_cart_quantities() {
    if ( WC()->cart->is_empty() ) return;
   $master_product_id = 20;
   $in_cart = false;
   foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
      if ( $master_product_id === $cart_item['product_id'] ) {
         $qty = $cart_item['quantity'];
         $in_cart = true;
         break;
      }
   }
   if ( ! $in_cart ) return;
   foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
      if ( $master_product_id !== $cart_item['product_id'] ) {
         WC()->cart->set_quantity( $cart_item_key, $qty );
      }
   }     
}

Advertisement

Answer

I took an interest in this as the problem is quite tricky to fix. In order for you to accomplish what you are asking, you need to know which quantity changed. Which is a big issue as I don’t think there is any hook on the PHP side that can tell you this.

This leaves us with one option from what I can see, and that is to inject some JavaScript into the bottom of the cart page that directly listens for the change. Here is an attempt I wrote to see if I could get it working:

function bbloomer_cart_quantity_js() {
    ?>
    <script>    
        jQuery(function($) {
            // hook an event fired when an quantity input box changes on the cart.
            $('.woocommerce').on('change', 'input.qty', function(e) {
                var product_ids = [84647, 84648];   // the synced ids.
                var m_input = $(e.target);          // get the input box that changed.
                var m_qty = m_input.val();          // record the new quantity.
                var m_row = m_input.parents('tr');  // get the cart item row.
                var m_product_id = m_row.find('a.remove[data-product_id]').data('product_id');

                // dont do anything if not the right product.
                if (!product_ids.includes(m_product_id)) return;

                // loop over the remove links to discover product_id matches.
                $('.shop_table a.remove[data-product_id]').each(function(index, element) {
                    var c_link = $(element);
                    var c_row = c_link.parents('tr');
                    var c_product_id = c_link.data('product_id');
                    if (product_ids.includes(c_product_id) && c_product_id != m_product_id) {
                        c_row.find('input.qty').val(m_qty);
                    }
                });

                // clear the last timeout we fired if any, stops too many ajax calls.
                if (window.timeout !== undefined) {
                    clearTimeout(timeout);
                }
            
                // use a timeout to delay and prevent too many ajax updates.
                window.timeout = setTimeout(function() {
                    // update the cart via ajax!
                    $("[name='update_cart']").trigger("click");
                }, 200 );
            });
        });
    </script>
    <?php
}
add_action('woocommerce_after_cart', 'bbloomer_cart_quantity_js');

This absolutely can be cleaned up further and made more efficient, however I’ve tried to comment as much of it as possible so you can see why I did what I did. I also tested this on a site I’m hosting and it worked, so it should work for you.

User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement