Search code examples
phpwordpresswoocommerceproductcart

WooCommerce Add to cart, add second product from a custom select field


What I need

I want to make a simple implementation of product add-ons without using heavyweight plugins which are overkill for my needs. Also I want to be able to keep track of add-ons inventory which is not offered by any of plugins. To make things a bit clearer I can say that the main product is a pendant and add-on is a chain. The customer should be able to select the chain on pendant's page, when he or she clicks add to cart button both items should be added to cart.

So I thought I could just use hidden single products as add-ons. The idea is to add a drop-down with my add-on products and get selected value via POST and add to cart it along with main product. So far so good.

What I did

Here's my code:

add_action( 'woocommerce_before_add_to_cart_button', 'chain_selection_field' );
function chain_selection_field() {
    global $product;
    $domain = 'woocommerce';
    $args = array(
    'sku' => 'SOME_TEXT',
    'stock_status' => 'instock',
    );
    $products = wc_get_products( $args );
    foreach ($products as $product) {
        $product_id = $product->get_id();
        $options[$product_id] = $product->get_name();
    }
    woocommerce_form_field('chain_type', array(
        'type'          => 'select',
        'label'         => __('Chain type selection', $domain),
        'required'      => true,
        'options'       => $options,
    ),'');
}

add_action('woocommerce_add_to_cart', 'product_option_add_to_cart');
function product_option_add_to_cart() {
    $product_id = $_POST['chain_type'];
    $found = false;
    if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
        foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
            $_product = $values['data'];
            if ( $_product->id == $product_id )
                $found = true;
        }
        if ( ! $found )
            WC()->cart->add_to_cart( $product_id );
    } else {
        WC()->cart->add_to_cart( $product_id );
    }
}

Add to cart implementation is taken from here - How to add filter or hook for "woocommerce_add_to_cart"

What's the problem?

Actually there are a lot of problems:

  1. As soon as I select some value in drop-down it is kept forever. Even after refreshing the page it is still added to cart even if I choose another value. Looks like I have to clear POST value somehow. I tried adding $_POST = array(); or unset($_POST); in the end of add to cart function but that didn't work.
  2. Main product is not added to cart - when I hit "add to cart" only add-on product is added to cart. I checked add to cart function separately with preset product ID and found that it works as expected and both product are added to cart.

Solution

  • The problem was that I used global variable $product in foreach loop. So it was breaking loop logics and using global $product instead of desired array value. So all it takes to make the code work is changing foreach loop code to something like:

    foreach ($products as $single_product) {
            $product_id = $single_product->get_id();
            $options[$product_id] = $single_product->get_name();
    

    Otherwise the code is working fine, although I have to make a few remarks:

    • the code will only work if inserted via hooks which are called inside <form> ... </form> submitted by pressing add to cart button, i.e. woocommerce_before_add_to_cart_button, woocommerce_before_add_to_cart_quantity and woocommerce_after_add_to_cart_quantity (you can check it yourself in the template - https://github.com/woocommerce/woocommerce/blob/4.1.0/templates/single-product/add-to-cart/simple.php) for simple product and also a few hooks called inside variation loop vor variable product which can be found here - https://github.com/woocommerce/woocommerce/blob/4.1.0/templates/single-product/add-to-cart/variable.php
    • when inserted via woocommerce_before_add_to_cart_button input field is placed inbetween product stock text and add to cart button which is not the best place and it looks like you either have to edit template files or use JS to achieve better placement with current WC (4.1) hooks.
    • wc_get_products () function is rather slow. In my test it was 10 times slower than similar query made through $wpdb->get_results
      • it is better to insert product_option_add_to_cart() custom function code into if (isset($_POST['chain_type'])) { ... } to prevent PHP notices in case there's no $_POST value for some reason