Sean Goudarzi
Sean Goudarzi

Reputation: 1544

Adding multiple items to WooCommerce cart at once

I have 3 IDs of the different items that I want to add to my shopping cart. I could use https://url.com/shop/cart/?add-to-cart=3001 but when I want to add 3 items I can't do it. Is there any function/script I can add to add this ability to my shopping website?

I tried to add an & after the add-to-cart and tried to add a new value but GETs get overridden right?: https://url.com/shop/cart/?add-to-cart=3001&add-to-cart=2002&add-to-cart=1001

Upvotes: 12

Views: 49887

Answers (13)

Alex BOURG
Alex BOURG

Reputation: 69

If you tried all the code and didn't work, using this code ; )

/**
 * Add multiple variable products to the cart using URL parameters.
 * 
 * @author Alex Bourg
 * @company IT Expert Now
 * @company_url https://www.itexpertnow.com
 *
 * The function expects two URL parameters:
 * - 'add-to-cart': a comma-separated list of product IDs to add to the cart.
 * - 'quantities': a comma-separated list of quantities corresponding to each product ID.
 *
 * The function adds each product to the cart with the corresponding quantity.
 * If the number of product IDs and quantities do not match, the function does nothing.
 */
function add_multiple_products_to_cart() {
    // Check if 'add-to-cart' and 'quantities' parameters are present in the URL.
    if ( isset( $_GET['add-to-cart'] ) && isset( $_GET['quantities'] ) ) {
        // Split the 'add-to-cart' and 'quantities' parameters into arrays.
        $product_ids = explode( ',', $_GET['add-to-cart'] );
        $quantities = explode( ',', $_GET['quantities'] );

        // Check if the count of product IDs matches the count of quantities.
        if ( count( $product_ids ) == count( $quantities ) ) {
            // Loop over the arrays and for each pair of product ID and quantity, add the product to the cart.
            for ( $i = 0; $i < count( $product_ids ); $i++ ) {
                $product_id = $product_ids[$i];
                $quantity = $quantities[$i];

                // Add the product to the cart using the WooCommerce function.
                WC()->cart->add_to_cart( $product_id, $quantity );
            }
        }
    }
}

// Attach our function to the 'init' action hook.
add_action( 'init', 'add_multiple_products_to_cart' );

Upvotes: 0

Anderson Viviel
Anderson Viviel

Reputation: 11

Considering correct syntax that would be the following to create the correct url.

www.website.com/cart/add-to-cart=[product1id]:[quantity],[product2id]:[quantity]

As I am extracting the product ID from a shortcode, and the quantity from another shortcode, how do I tell the system that if it is empty, don't show it.

The formula works fine, maybe because I use shortcode it fails and returns 0 which becomes 1, the code works for all quantity fields that have a value inserted, the url if it sees that it has no value, it returns 0 and adds the product as quantity 1.

function webroom_add_multiple_products_to_cart( $url = false ) {
    // Make sure WC is installed, and add-to-cart qauery arg exists, and contains at least one comma.
    if ( ! class_exists( 'WC_Form_Handler' ) || empty( $_REQUEST['add-to-cart'] ) || false === strpos( $_REQUEST['add-to-cart'], ',' ) ) {
        return;
    }

    // Remove WooCommerce's hook, as it's useless (doesn't handle multiple products).
    remove_action( 'wp_loaded', array( 'WC_Form_Handler', 'add_to_cart_action' ), 20 );

    $product_ids = explode( ',', $_REQUEST['add-to-cart'] );
    $count       = count( $product_ids );
    $number      = 0;

    foreach ( $product_ids as $id_and_quantity ) {
        // Check for quantities defined in curie notation (<product_id>:<product_quantity>)
        
        $id_and_quantity = explode( ':', $id_and_quantity );
        $product_id = $id_and_quantity[0];

        $_REQUEST['quantity'] = ! empty( $id_and_quantity[1] ) ? absint( $id_and_quantity[1] ) : 1;

        if ( ++$number === $count ) {
            // Ok, final item, let's send it back to woocommerce's add_to_cart_action method for handling.
            $_REQUEST['add-to-cart'] = $product_id;

            return WC_Form_Handler::add_to_cart_action( $url );
        }

        $product_id        = apply_filters( 'woocommerce_add_to_cart_product_id', absint( $product_id ) );
        $was_added_to_cart = false;
        $adding_to_cart    = wc_get_product( $product_id );

        if ( ! $adding_to_cart ) {
            continue;
        }

        $add_to_cart_handler = apply_filters( 'woocommerce_add_to_cart_handler', $adding_to_cart->get_type(), $adding_to_cart );

        // Variable product handling
        if ( 'variable' === $add_to_cart_handler ) {
            woo_hack_invoke_private_method( 'WC_Form_Handler', 'add_to_cart_handler_variable', $product_id );

        // Grouped Products
        } elseif ( 'grouped' === $add_to_cart_handler ) {
            woo_hack_invoke_private_method( 'WC_Form_Handler', 'add_to_cart_handler_grouped', $product_id );

        // Custom Handler
        } elseif ( has_action( 'woocommerce_add_to_cart_handler_' . $add_to_cart_handler ) ){
            do_action( 'woocommerce_add_to_cart_handler_' . $add_to_cart_handler, $url );

        // Simple Products
        } else {
            woo_hack_invoke_private_method( 'WC_Form_Handler', 'add_to_cart_handler_simple', $product_id );
        }
    }
}

// Fire before the WC_Form_Handler::add_to_cart_action callback.
add_action( 'wp_loaded', 'webroom_add_multiple_products_to_cart', 15 );


/**
 * Invoke class private method
 *
 * @since   0.1.0
 *
 * @param   string $class_name
 * @param   string $methodName
 *
 * @return  mixed
 */
function woo_hack_invoke_private_method( $class_name, $methodName ) {
    if ( version_compare( phpversion(), '5.3', '<' ) ) {
        throw new Exception( 'PHP version does not support ReflectionClass::setAccessible()', __LINE__ );
    }

    $args = func_get_args();
    unset( $args[0], $args[1] );
    $reflection = new ReflectionClass( $class_name );
    $method = $reflection->getMethod( $methodName );
    $method->setAccessible( true );

    //$args = array_merge( array( $class_name ), $args );
    $args = array_merge( array( $reflection ), $args );
    return call_user_func_array( array( $method, 'invoke' ), $args );
}

Here I found the code and then I discovered this chat where they were talking about the same thing.

Upvotes: 0

odedta
odedta

Reputation: 2478

I've tried these solutions but nothing worked since init ran a few times and the products were adding a few times as well so the quantities were off.
In addition, I wanted to have the ability to add a coupon from the URL as well...

/*
 * Init function
 * 1. Apply coupon code from URL
 * 2. Add to cart: Multiple products add via URL
 */
function nd_template_redirect() {
    if( !isset( WC()->session ) || ( isset( WC()->session ) && !WC()->session->has_session() ) ) {
        WC()->session->set_customer_session_cookie( true );
    }
    
    $product_ids = array();
    $quantities  = array();
    
    if( isset( $_GET['add-to-cart'] ) ) {
        if( false !== strpos( $_GET['add-to-cart'], ',' ) ) {
            $product_ids = explode( ',', $_GET['add-to-cart'] );
        }
    }
    
    if( isset( $_GET['quantity'] ) ) {
        if( false !== strpos( $_GET['quantity'], ',' ) ) {
            $quantities = explode( ',', $_GET['quantity'] );
        }
    }
    
    if( !empty( $product_ids ) ) {
        $products = array();
        
        for( $i = 0; $i < count( $product_ids ); $i++ ) {
            if( isset( $product_ids[ $i ] ) ) {
                $products[ $product_ids[ $i ] ] = isset( $quantities[ $i ] ) ? $quantities[ $i ] : 1;
            }
        }
        
        if( !empty( $products ) ) {
            foreach( $products as $key => $value ) {
                WC()->cart->add_to_cart( $key, $value );
            }
        }
    }
    
    if( isset( $_GET['coupon_code'] ) && empty( WC()->session->get( 'added_coupon_code' ) ) ) {
        $coupon_code = esc_attr( $_GET['coupon_code'] );
        
        if( !empty( $coupon_code ) && !WC()->cart->has_discount( $coupon_code ) ) {
            WC()->cart->add_discount( $coupon_code );
            
            WC()->session->set( 'added_coupon_code', true );
        }
    }
}
add_action( 'template_redirect', 'nd_template_redirect' );

/*
 * Clear session variables on thank you page.
 */
function nd_woocommerce_thankyou( $order_id ) {
    WC()->session->__unset( 'added_coupon_code' );
    WC()->session->__unset( 'added_products_to_cart' );
}
add_action( 'woocommerce_thankyou', 'nd_woocommerce_thankyou' );

Example usage:
https://example.com/cart/?add-to-cart=19737,19713&quantity=2,1&coupon_code=TEST

A few notes:

  • This works with or without quantity and coupon code parameters, if you choose to omit them the quantity of the products that will be added to the cart will be 1.
  • The product ids have to be separated using commas without spaces.
  • Match the product ids with the quantity in the correct order, in this example 2 units of product 19737 will be added to the cart.
  • This solution works only for URLs that send the user to the cart page.

Upvotes: 0

bar golan
bar golan

Reputation: 11

support variations and quantities using add-to-cart and quantities

// Make sure WC is installed, and add-to-cart qauery arg exists, and contains at least one comma.
if ( ! class_exists( 'WC_Form_Handler' ) || empty( $_REQUEST['add-to-cart'] ) || false === strpos( $_REQUEST['add-to-cart'], ',' ) ) {
    return;
}

remove_action( 'wp_loaded', array( 'WC_Form_Handler', 'add_to_cart_action' ), 20 );

$product_ids = explode( ',', $_REQUEST['add-to-cart'] );
$quantities = explode( ',', $_REQUEST['quantities'] );
$count       = count( $product_ids );
$number      = 0;

foreach ( $product_ids as $product_id ) {
    if ( ++$number === $count ) {
        // Ok, final item, let's send it back to woocommerce's add_to_cart_action method for handling.
        $_REQUEST['add-to-cart'] = $product_id;

        return WC_Form_Handler::add_to_cart_action();
    }

    $product_id        = apply_filters( 'woocommerce_add_to_cart_product_id', absint( $product_id ) );
    $was_added_to_cart = false;

    $adding_to_cart    = wc_get_product( $product_id );

    if ( ! $adding_to_cart ) {
        continue;
    }

    if ( $adding_to_cart->is_type( 'simple' ) ) {
        $_REQUEST['quantity'] = ! empty( $quantities[$number] ) ? absint( $quantities[$number] ) : 1;
        $quantity          = empty( $quantities[$number - 1] ) ? 1 : wc_stock_amount(  $quantities[$number - 1] );
        // quantity applies to all products atm
        //$quantity          = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( $_REQUEST['quantity'] );
        $passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity );

        if ( $passed_validation && false !== WC()->cart->add_to_cart( $product_id, $quantity ) ) {
            wc_add_to_cart_message( array( $product_id => $quantity ), true );
        }

    } else {

        $variation_id       = empty( $_REQUEST['variation_id'] ) ? '' : absint( wp_unslash( $_REQUEST['variation_id'] ) );
        $quantity           = empty( $quantities[$number - 1] ) ? 1 : wc_stock_amount( wp_unslash( $quantities[$number - 1] ) ); // WPCS: sanitization ok.
        $missing_attributes = array();
        $variations         = array();
        $adding_to_cart     = wc_get_product( $product_id );

        if ( ! $adding_to_cart ) {
          continue;
        }

        // If the $product_id was in fact a variation ID, update the variables.
        if ( $adding_to_cart->is_type( 'variation' ) ) {
          $variation_id   = $product_id;
          $product_id     = $adding_to_cart->get_parent_id();
          $adding_to_cart = wc_get_product( $product_id );

          if ( ! $adding_to_cart ) {
            continue;
          }
        }

        // Gather posted attributes.
        $posted_attributes = array();

        foreach ( $adding_to_cart->get_attributes() as $attribute ) {
          if ( ! $attribute['is_variation'] ) {
            continue;
          }
          $attribute_key = 'attribute_' . sanitize_title( $attribute['name'] );

          if ( isset( $_REQUEST[ $attribute_key ] ) ) {
            if ( $attribute['is_taxonomy'] ) {
              // Don't use wc_clean as it destroys sanitized characters.
              $value = sanitize_title( wp_unslash( $_REQUEST[ $attribute_key ] ) );
            } else {
              $value = html_entity_decode( wc_clean( wp_unslash( $_REQUEST[ $attribute_key ] ) ), ENT_QUOTES, get_bloginfo( 'charset' ) ); // WPCS: sanitization ok.
            }

            $posted_attributes[ $attribute_key ] = $value;
          }
        }

        // If no variation ID is set, attempt to get a variation ID from posted attributes.
        if ( empty( $variation_id ) ) {
          $data_store   = WC_Data_Store::load( 'product' );
          $variation_id = $data_store->find_matching_product_variation( $adding_to_cart, $posted_attributes );
        }

        // Do we have a variation ID?
        if ( empty( $variation_id ) ) {
          throw new Exception( __( 'Please choose product options&hellip;', 'woocommerce' ) );
        }

        // Check the data we have is valid.
        $variation_data = wc_get_product_variation_attributes( $variation_id );

        foreach ( $adding_to_cart->get_attributes() as $attribute ) {
          if ( ! $attribute['is_variation'] ) {
            continue;
          }

          // Get valid value from variation data.
          $attribute_key = 'attribute_' . sanitize_title( $attribute['name'] );
          $valid_value   = isset( $variation_data[ $attribute_key ] ) ? $variation_data[ $attribute_key ]: '';

          /**
           * If the attribute value was posted, check if it's valid.
           *
           * If no attribute was posted, only error if the variation has an 'any' attribute which requires a value.
           */
          if ( isset( $posted_attributes[ $attribute_key ] ) ) {
            $value = $posted_attributes[ $attribute_key ];

            // Allow if valid or show error.
            if ( $valid_value === $value ) {
              $variations[ $attribute_key ] = $value;
            } elseif ( '' === $valid_value && in_array( $value, $attribute->get_slugs() ) ) {
              // If valid values are empty, this is an 'any' variation so get all possible values.
              $variations[ $attribute_key ] = $value;
            } else {
              throw new Exception( sprintf( __( 'Invalid value posted for %s', 'woocommerce' ), wc_attribute_label( $attribute['name'] ) ) );
            }
          } elseif ( '' === $valid_value ) {
            $missing_attributes[] = wc_attribute_label( $attribute['name'] );
          }
        }
        if ( ! empty( $missing_attributes ) ) {
          throw new Exception( sprintf( _n( '%s is a required field', '%s are required fields', count( $missing_attributes ), 'woocommerce' ), wc_format_list_of_items( $missing_attributes ) ) );
        }

      $passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity, $variation_id, $variations );

      if ( $passed_validation && false !== WC()->cart->add_to_cart( $product_id, $quantity, $variation_id, $variations ) ) {
        wc_add_to_cart_message( array( $product_id => $quantity ), true );
      }
    }
}

}

Upvotes: 0

Nicolas Vills
Nicolas Vills

Reputation: 11

If you don't want to code anything, you can create a grouped product with all the products you want to add to the cart added as Linked Products.

Then the url will be as follow:

https://url.com/shop/cart/?add-to-cart=3001&quantity[2002]=1&quantity[1001]=1
  • 3001 = Grouped Product id
  • 2002 = Linked Product1 id
  • 1001 = Linked Product2 id

Don't forget to specify quantities.

Also, you can change the catalog visibility to "hidden" so that grouped product doesn't appear in your store.

Upvotes: 1

Howdy_McGee
Howdy_McGee

Reputation: 10635

WooCommerce 4.0, an easier way to do this is to use the template_redirect hook which fires early off in the process. In theory you could decode a URL and add multiple items to cart or POST a form and process the POST. The simplest form is:

add_action( 'template_redirect', function() {
    
    if( ! isset( $_POST['products'] ) ) {
        return;
    }
    
    $products = array_filter( $_POST['products'] );
    
    foreach( $products as $product_id => $quantity ) {
        WC()->cart->add_to_cart( $product_id, $quantity );
    }
    
} );

Upvotes: 3

Fabio Tortorella
Fabio Tortorella

Reputation: 43

The correct syntax is

add-to-cart=product1id:quantity,product2id:quantity  

This is the code https://www.webroomtech.com/woocommerce-add-multiple-products-to-cart-via-url/

They don't show this syntax for quantities on the tutorial, I found it looking at the code.

Upvotes: 3

Amit be
Amit be

Reputation: 469

Same answer as others, with support for multiple quantities.

Example url: http://store.amitbend.com/cart/?add-to-cart=188,187,189&quantities=3,2,1


function woocommerce_maybe_add_multiple_products_to_cart() {
// Make sure WC is installed, and add-to-cart qauery arg exists, and contains at least one comma.
if ( ! class_exists( 'WC_Form_Handler' ) || empty( $_REQUEST['add-to-cart'] ) || false === strpos( $_REQUEST['add-to-cart'], ',' ) ) {
    return;
}

// Remove WooCommerce's hook, as it's useless (doesn't handle multiple products).
remove_action( 'wp_loaded', array( 'WC_Form_Handler', 'add_to_cart_action' ), 20 );

$product_ids = explode( ',', $_REQUEST['add-to-cart'] );
$quantities = explode( ',', $_REQUEST['quantities'] );

$count       = count( $product_ids );
$number      = 0;

foreach ( $product_ids as $product_id ) {
    if ( ++$number === $count ) {
        // Ok, final item, let's send it back to woocommerce's add_to_cart_action method for handling.
        $_REQUEST['add-to-cart'] = $product_id;

        return WC_Form_Handler::add_to_cart_action();
    }

    $product_id        = apply_filters( 'woocommerce_add_to_cart_product_id', absint( $product_id ) );
    $was_added_to_cart = false;
    $adding_to_cart    = wc_get_product( $product_id );

    if ( ! $adding_to_cart ) {
        continue;
    }

    $add_to_cart_handler = apply_filters( 'woocommerce_add_to_cart_handler', $adding_to_cart->product_type, $adding_to_cart );

    /*
     * Sorry.. if you want non-simple products, you're on your own.
     *
     * Related: WooCommerce has set the following methods as private:
     * WC_Form_Handler::add_to_cart_handler_variable(),
     * WC_Form_Handler::add_to_cart_handler_grouped(),
     * WC_Form_Handler::add_to_cart_handler_simple()
     *
     * Why you gotta be like that WooCommerce?
     */
    if ( 'simple' !== $add_to_cart_handler ) {
        continue;
    }
//         $_REQUEST['quantity'] = ! empty( $id_and_quantity[1] ) ? absint( $id_and_quantity[1] ) : 1;
$_REQUEST['quantity'] = ! empty( $quantities[$number] ) ? absint( $quantities[$number] ) : 1;
    $quantity          = empty( $quantities[$number - 1] ) ? 1 : wc_stock_amount(  $quantities[$number - 1] );
//     $quantity          = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( $_REQUEST['quantity'] );
    $passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity );

    if ( $passed_validation && false !== WC()->cart->add_to_cart( $product_id, $quantity ) ) {
        wc_add_to_cart_message( array( $product_id => $quantity ), true );
    }
}
}

 // Fire before the WC_Form_Handler::add_to_cart_action callback.
 add_action( 'wp_loaded',        'woocommerce_maybe_add_multiple_products_to_cart', 15 );
// remove "added to cart" notice
add_filter( 'wc_add_to_cart_message_html', '__return_false' );

Upvotes: 3

Ali
Ali

Reputation: 504

Thanks to @thatgerhard and dsgnwrks
I found a solution for support variations:

function woocommerce_maybe_add_multiple_products_to_cart() {
  // Make sure WC is installed, and add-to-cart qauery arg exists, and contains at least one comma.
  if ( ! class_exists( 'WC_Form_Handler' ) || empty( $_REQUEST['add-to-cart'] ) || false === strpos( $_REQUEST['add-to-cart'], ',' ) ) {
      return;
  }

  remove_action( 'wp_loaded', array( 'WC_Form_Handler', 'add_to_cart_action' ), 20 );

  $product_ids = explode( ',', $_REQUEST['add-to-cart'] );
  $count       = count( $product_ids );
  $number      = 0;

  foreach ( $product_ids as $product_id ) {
      if ( ++$number === $count ) {
          // Ok, final item, let's send it back to woocommerce's add_to_cart_action method for handling.
          $_REQUEST['add-to-cart'] = $product_id;

          return WC_Form_Handler::add_to_cart_action();
      }

      $product_id        = apply_filters( 'woocommerce_add_to_cart_product_id', absint( $product_id ) );
      $was_added_to_cart = false;

      $adding_to_cart    = wc_get_product( $product_id );

      if ( ! $adding_to_cart ) {
          continue;
      }

      if ( $adding_to_cart->is_type( 'simple' ) ) {

          // quantity applies to all products atm
          $quantity          = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( $_REQUEST['quantity'] );
          $passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity );

          if ( $passed_validation && false !== WC()->cart->add_to_cart( $product_id, $quantity ) ) {
              wc_add_to_cart_message( array( $product_id => $quantity ), true );
          }

      } else {

          $variation_id       = empty( $_REQUEST['variation_id'] ) ? '' : absint( wp_unslash( $_REQUEST['variation_id'] ) );
          $quantity           = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( wp_unslash( $_REQUEST['quantity'] ) ); // WPCS: sanitization ok.
          $missing_attributes = array();
          $variations         = array();
          $adding_to_cart     = wc_get_product( $product_id );

          if ( ! $adding_to_cart ) {
            continue;
          }

          // If the $product_id was in fact a variation ID, update the variables.
          if ( $adding_to_cart->is_type( 'variation' ) ) {
            $variation_id   = $product_id;
            $product_id     = $adding_to_cart->get_parent_id();
            $adding_to_cart = wc_get_product( $product_id );

            if ( ! $adding_to_cart ) {
              continue;
            }
          }

          // Gather posted attributes.
          $posted_attributes = array();

          foreach ( $adding_to_cart->get_attributes() as $attribute ) {
            if ( ! $attribute['is_variation'] ) {
              continue;
            }
            $attribute_key = 'attribute_' . sanitize_title( $attribute['name'] );

            if ( isset( $_REQUEST[ $attribute_key ] ) ) {
              if ( $attribute['is_taxonomy'] ) {
                // Don't use wc_clean as it destroys sanitized characters.
                $value = sanitize_title( wp_unslash( $_REQUEST[ $attribute_key ] ) );
              } else {
                $value = html_entity_decode( wc_clean( wp_unslash( $_REQUEST[ $attribute_key ] ) ), ENT_QUOTES, get_bloginfo( 'charset' ) ); // WPCS: sanitization ok.
              }

              $posted_attributes[ $attribute_key ] = $value;
            }
          }

          // If no variation ID is set, attempt to get a variation ID from posted attributes.
          if ( empty( $variation_id ) ) {
            $data_store   = WC_Data_Store::load( 'product' );
            $variation_id = $data_store->find_matching_product_variation( $adding_to_cart, $posted_attributes );
          }

          // Do we have a variation ID?
          if ( empty( $variation_id ) ) {
            throw new Exception( __( 'Please choose product options&hellip;', 'woocommerce' ) );
          }

          // Check the data we have is valid.
          $variation_data = wc_get_product_variation_attributes( $variation_id );

          foreach ( $adding_to_cart->get_attributes() as $attribute ) {
            if ( ! $attribute['is_variation'] ) {
              continue;
            }

            // Get valid value from variation data.
            $attribute_key = 'attribute_' . sanitize_title( $attribute['name'] );
            $valid_value   = isset( $variation_data[ $attribute_key ] ) ? $variation_data[ $attribute_key ]: '';

            /**
             * If the attribute value was posted, check if it's valid.
             *
             * If no attribute was posted, only error if the variation has an 'any' attribute which requires a value.
             */
            if ( isset( $posted_attributes[ $attribute_key ] ) ) {
              $value = $posted_attributes[ $attribute_key ];

              // Allow if valid or show error.
              if ( $valid_value === $value ) {
                $variations[ $attribute_key ] = $value;
              } elseif ( '' === $valid_value && in_array( $value, $attribute->get_slugs() ) ) {
                // If valid values are empty, this is an 'any' variation so get all possible values.
                $variations[ $attribute_key ] = $value;
              } else {
                throw new Exception( sprintf( __( 'Invalid value posted for %s', 'woocommerce' ), wc_attribute_label( $attribute['name'] ) ) );
              }
            } elseif ( '' === $valid_value ) {
              $missing_attributes[] = wc_attribute_label( $attribute['name'] );
            }
          }
          if ( ! empty( $missing_attributes ) ) {
            throw new Exception( sprintf( _n( '%s is a required field', '%s are required fields', count( $missing_attributes ), 'woocommerce' ), wc_format_list_of_items( $missing_attributes ) ) );
          }

        $passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity, $variation_id, $variations );

        if ( $passed_validation && false !== WC()->cart->add_to_cart( $product_id, $quantity, $variation_id, $variations ) ) {
          wc_add_to_cart_message( array( $product_id => $quantity ), true );
        }
      }
  }
}
add_action( 'wp_loaded', 'woocommerce_maybe_add_multiple_products_to_cart', 15 );

Upvotes: 8

Sushil Adhikari
Sushil Adhikari

Reputation: 794

Here I had forced $_REQUEST[ 'add-to-cart' ] to take the intended product id. Overwriting global variable doesn't sound good approach but it keeps DRY approach and also make all native function available to user.

add_action( 'wp_loaded', 'add_multiple_to_cart_action', 20 );

function add_multiple_to_cart_action() {
    if ( ! isset( $_REQUEST['multiple-item-to-cart'] ) || false === strpos( wp_unslash( $_REQUEST['multiple-item-to-cart'] ), '|' ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
            return;
        }

    wc_nocache_headers();

    $product_ids        = apply_filters( 'woocommerce_add_to_cart_product_id', wp_unslash( $_REQUEST['multiple-item-to-cart'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
    $product_ids = explode( '|', $product_ids );
    if( ! is_array( $product_ids ) ) return;

    $product_ids = array_map( 'absint', $product_ids );
    $was_added_to_cart = false;
    $last_product_id = end($product_ids);
    //stop re-direction
    add_filter( 'woocommerce_add_to_cart_redirect', '__return_false' );
    foreach ($product_ids as $index => $product_id ) {
        $product_id = absint(  $product_id  );
        if( empty( $product_id ) ) continue;
        $_REQUEST['add-to-cart'] = $product_id;
        if( $product_id === $last_product_id ) {

            add_filter( 'option_woocommerce_cart_redirect_after_add', function() { 
                return 'yes'; 
            } );
        } else {
            add_filter( 'option_woocommerce_cart_redirect_after_add', function() { 
                return 'no'; 
            } );
        }

        WC_Form_Handler::add_to_cart_action();
    }
}

Example:

https://your-domain/cart?multiple-item-to-cart=68|69 Thanks

Upvotes: 4

Nate
Nate

Reputation: 21

I took a different approach to this. Instead of killing the WC_Form_Handler, I use it! I have it run for each product. This seems simpler to me.

You can use this alone, or also with the regular add-to-cart.

http://example.com?add-more-to-cart=1000,10001,10002

http://example.com?add-to-cart=1000&add-more-to-cart=10001,10002

class add_more_to_cart {

    private $prevent_redirect = false; //used to prevent WC from redirecting if we have more to process

    function __construct() {
        if ( ! isset( $_REQUEST[ 'add-more-to-cart' ] ) ) return; //don't load if we don't have to
        $this->prevent_redirect = 'no'; //prevent WC from redirecting so we can process additional items
        add_action( 'wp_loaded', [ $this, 'add_more_to_cart' ], 21 ); //fire after WC does, so we just process extra ones
        add_action( 'pre_option_woocommerce_cart_redirect_after_add', [ $this, 'intercept_option' ], 9000 ); //intercept the WC option to force no redirect
    }

    function intercept_option() {
        return $this->prevent_redirect;
    }

    function add_more_to_cart() {
        $product_ids = explode( ',', $_REQUEST['add-more-to-cart'] );
        $count       = count( $product_ids );
        $number      = 0;

        foreach ( $product_ids as $product_id ) {
            if ( ++$number === $count ) $this->prevent_redirect = false; //this is the last one, so let WC redirect if it wants to.
            $_REQUEST['add-to-cart'] = $product_id; //set the next product id
            WC_Form_Handler::add_to_cart_action(); //let WC run its own code
        }
    }
}
new add_more_to_cart;

Upvotes: 0

thatgerhard
thatgerhard

Reputation: 303

In case anyone else comes by looking for this function like I did. I updated the top comment's code allowing it to work with woocommerce 3.5.3

    // adds support for multi add to cart for 3rd party cart plugin
function woocommerce_maybe_add_multiple_products_to_cart() {
    // Make sure WC is installed, and add-to-cart qauery arg exists, and contains at least one comma.
    if ( ! class_exists( 'WC_Form_Handler' ) || empty( $_REQUEST['add-to-cart'] ) || false === strpos( $_REQUEST['add-to-cart'], ',' ) ) {
        return;
    }

    // Remove WooCommerce's hook, as it's useless (doesn't handle multiple products).
    remove_action( 'wp_loaded', array( 'WC_Form_Handler', 'add_to_cart_action' ), 20 );

    $product_ids = explode( ',', $_REQUEST['add-to-cart'] );
    $count       = count( $product_ids );
    $number      = 0;

    foreach ( $product_ids as $product_id ) {
        if ( ++$number === $count ) {
            // Ok, final item, let's send it back to woocommerce's add_to_cart_action method for handling.
            $_REQUEST['add-to-cart'] = $product_id;

            return WC_Form_Handler::add_to_cart_action();
        }

        $product_id        = apply_filters( 'woocommerce_add_to_cart_product_id', absint( $product_id ) );
        $was_added_to_cart = false;

        $adding_to_cart    = wc_get_product( $product_id );

        if ( ! $adding_to_cart ) {
            continue;
        }

        // only works for simple atm
        if ( $adding_to_cart->is_type( 'simple' ) ) {

            // quantity applies to all products atm
            $quantity          = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( $_REQUEST['quantity'] );
            $passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity );

            if ( $passed_validation && false !== WC()->cart->add_to_cart( $product_id, $quantity ) ) {
                wc_add_to_cart_message( array( $product_id => $quantity ), true );
            }

        }
    }
}
add_action( 'wp_loaded', 'woocommerce_maybe_add_multiple_products_to_cart', 15 );

Upvotes: 8

Sean Goudarzi
Sean Goudarzi

Reputation: 1544

I found the answer!

Simply add the following script to your theme's functions.php:

function woocommerce_maybe_add_multiple_products_to_cart() {
// Make sure WC is installed, and add-to-cart qauery arg exists, and contains at least one comma.
if ( ! class_exists( 'WC_Form_Handler' ) || empty( $_REQUEST['add-to-cart'] ) || false === strpos( $_REQUEST['add-to-cart'], ',' ) ) {
    return;
}

// Remove WooCommerce's hook, as it's useless (doesn't handle multiple products).
remove_action( 'wp_loaded', array( 'WC_Form_Handler', 'add_to_cart_action' ), 20 );

$product_ids = explode( ',', $_REQUEST['add-to-cart'] );
$count       = count( $product_ids );
$number      = 0;

foreach ( $product_ids as $product_id ) {
    if ( ++$number === $count ) {
        // Ok, final item, let's send it back to woocommerce's add_to_cart_action method for handling.
        $_REQUEST['add-to-cart'] = $product_id;

        return WC_Form_Handler::add_to_cart_action();
    }

    $product_id        = apply_filters( 'woocommerce_add_to_cart_product_id', absint( $product_id ) );
    $was_added_to_cart = false;
    $adding_to_cart    = wc_get_product( $product_id );

    if ( ! $adding_to_cart ) {
        continue;
    }

    $add_to_cart_handler = apply_filters( 'woocommerce_add_to_cart_handler', $adding_to_cart->product_type, $adding_to_cart );

    /*
     * Sorry.. if you want non-simple products, you're on your own.
     *
     * Related: WooCommerce has set the following methods as private:
     * WC_Form_Handler::add_to_cart_handler_variable(),
     * WC_Form_Handler::add_to_cart_handler_grouped(),
     * WC_Form_Handler::add_to_cart_handler_simple()
     *
     * Why you gotta be like that WooCommerce?
     */
    if ( 'simple' !== $add_to_cart_handler ) {
        continue;
    }

    // For now, quantity applies to all products.. This could be changed easily enough, but I didn't need this feature.
    $quantity          = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( $_REQUEST['quantity'] );
    $passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity );

    if ( $passed_validation && false !== WC()->cart->add_to_cart( $product_id, $quantity ) ) {
        wc_add_to_cart_message( array( $product_id => $quantity ), true );
    }
}
}

 // Fire before the WC_Form_Handler::add_to_cart_action callback.
 add_action( 'wp_loaded',        'woocommerce_maybe_add_multiple_products_to_cart', 15 );

And then you can simply use http://shop.com/shop/cart/?add-to-cart=3001,3282 to add multiple items at once. Put a comma between different IDs.

Thanks to dsgnwrks for the solution.

Upvotes: 19

Related Questions