1

Situation

My website offers two products. If users buy Product A, they get access to all ebooks. If users buy Product B, they would be allowed to choose one ebook. In WooCommerce, I created ebooks as variations within Product B.

Product A ID = 477

Product B ID = 297 (https://i.stack.imgur.com/reBpk.png). While 297 is the main ID, I believe I should be using variation IDs eg. 1483, 997 (https://i.stack.imgur.com/wCIu9.png).

Problem I am trying to solve

I would like both products to be mutually exclusive. Afterall, if a user bought access to all ebooks (ie. product A), then they would not need to buy individual books under Product B.

End result

I tried some code (as shown below) and it does not work at all. Meaning I am still able to add Product A and variations of B together in the same cart.

Question 1: How do I solve this?

Question 2: Is there an easier way to not specify each and every Product B variation? It is an extremely long list and is not a scalable solution in the long term.

What I tried

This appears to do what I want: Woocomerce Remove a specific cart items when adding to cart another specific items

I had to amend it slightly because of Product B variations.

add_action( 'woocommerce_add_to_cart', 'check_product_added_to_cart', 10, 6 );
function check_product_added_to_cart($cart_item_key, $product_id, $quantity, $variation_id, $variation, $cart_item_data) {

    // Set HERE your targeted product ID
    $target_product_ids1 = array(477);
    $target_product_ids2 = array(1483, 997,998,999,1000,1001);
    // Set HERE the  product ID to remove
    $item_id_to_remove1 = array(1483, 997,998,999,1000,1481);
    $item_id_to_remove2 = array(477);

    // Initialising some variables
    $has_item1 = $has_item2 = $is_product_id1 = $is_product_id2 = false;

    foreach( WC()->cart->get_cart() as $key => $item ){
        // Check if the item to be removed 1 is in cart
        if( in_array( $item['product_id'], $item_id_to_remove1) ){
            $has_item1 = true;
            $key_to_remove1 = $key;
        }
        // Check if the item to be removed 2 is in cart
        if( in_array( $item['product_id'], $item_id_to_remove2) ){
            $has_item2 = true;
            $key_to_remove2 = $key;
        }

        // Check if we added to cart any targeted product IDs 1
        if( in_array( $product_id, $target_product_ids1 ) ){
            $is_product_id1 = true;
        }

        // Check if we added to cart any targeted product IDs 2
        if( in_array( $product_id, $target_product_ids2 ) ){
            $is_product_id2 = true;
        }
    }

    if( $has_item1 && $is_product_id1 ){
        WC()->cart->remove_cart_item($key_to_remove1);

        // Optionaly displaying a notice for the removed item:
        wc_add_notice( __( 'The product 1 "blab bla" has been removed from cart.', 'theme_domain' ), 'notice' );
    }

    if( $has_item2 && $is_product_id2 ){
        WC()->cart->remove_cart_item($key_to_remove2);

        // Optionaly displaying a notice for the removed item:
        wc_add_notice( __( 'The product 2 "blab bla" has been removed from cart.', 'theme_domain' ), 'notice' );
    }
}
LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
xojijog684
  • 190
  • 2
  • 8

1 Answers1

1

There is a much simpler way using Add to cart validation hook this way:

add_filter( 'woocommerce_add_to_cart_validation', 'wc_add_to_cart_validation_filter_callback', 10, 3 );
function wc_add_to_cart_validation_filter_callback( $passed, $product_id, $quantity ) {
    if( WC()->cart->is_empty() )
        return $passed;

    $product_id1 = 37; // Single product Id
    $product_id2 = 19; // Variable product Id

    // Loop through cart items
    foreach( WC()->cart->get_cart() as $cart_item ) {
        if( ( $product_id = $product_id1 && $cart_item['product_id'] == $product_id2 )
        || ( $product_id = $product_id2 && $cart_item['product_id'] == $product_id1 ) ) {
            wc_add_notice( sprintf(
                __("This product can't not be purchased toggether with %s."),
                '"<strong>' . $cart_item['data']->get_name() . '</strong>"'
            ), 'error' );
            return false;
        }
    }

    return $passed;
}

Code goes in functions.php file of your active child theme (or active theme). Tested and works.


Addition (related to your comment):

Another way is to remove the first item silently when both products are in cart:

add_action('woocommerce_before_calculate_totals', 'keep_last_variation_from_variable_products', 10, 1 );
function keep_last_variation_from_variable_products( $cart ) {
    if ( is_admin() && ! defined( 'DOING_AJAX' ) )
        return;

    $product_id1 = 37; // Single product Id
    $product_id2 = 19; // Variable product Id

    $product_ids = array($product_id1, $product_id2);
    $items_keys  = array();

    // Loop through cart items
    foreach (  $cart->get_cart() as $cart_item_key => $cart_item ) {
        // If cart item matches with one of our defined product
        if ( in_array( $cart_item['product_id'], $product_ids ) ) {
            // Set the cart item key in an array (avoiding duplicates)
            $items_keys[$cart_item['product_id']] = $cart_item_key;
        }
    }

    // If both products are in cart just keep the last one silently
    if( count($items_keys) > 1 ) {
        $cart->remove_cart_item( reset($items_keys) ); // remove the first one
    }
}

Code goes in functions.php file of your active child theme (or active theme). Tested and works.

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
  • Hey LoicTheAztec, thanks for the reply. With your code, if Product A is in the cart, then Product B cannot be added at all (and vice versa). Would it be possible to have it such that if either product is added, then the other is automatically removed from cart? So if Product A is already in cart and Product B is added, then the Product A is removed from cart and Product B is added. Thanks. – xojijog684 Jul 16 '20 at 15:26
  • Works perfectly! thank you, Sir! I forgot to click accept and have now done so. – xojijog684 Jul 16 '20 at 17:45