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.sstatic.net/reBpk.png). While 297 is the main ID, I believe I should be using variation IDs eg. 1483, 997 (https://i.sstatic.net/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' );
}
}
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.