Skip to content

Instantly share code, notes, and snippets.

@daigo75
Created November 23, 2019 16:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save daigo75/35b69deaac23de1e5450c7ad1309bd0a to your computer and use it in GitHub Desktop.
Save daigo75/35b69deaac23de1e5450c7ad1309bd0a to your computer and use it in GitHub Desktop.
WooCommerce - Allow specific products in cart (WC 3.x)
/**
* Restricts which products can be added to the cart at the same time.
* Version for WooCommerce 3.x and later.
*
* HOW TO USE THIS CODE
* 1. Add the code to the bottom of your theme's functions.php file (see https://www.skyverge.com/blog/add-custom-code-to-wordpress/).
* 2. Set the IDs of the products that are allowed to be added to the cart at the same time.
* 3. Amend the message displayed to customers when products are unavailable after the specified
* products have been added to the cart (see function woocommerce_get_price_html(), below).
*
* GPL DISCLAIMER
* Because this code program is free of charge, there is no warranty for it, to the extent permitted by applicable law.
* Except when otherwise stated in writing the copyright holders and/or other parties provide the program "as is"
* without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of
* merchantability and fitness for a particular purpose. The entire risk as to the quality and performance of the program
* is with you. should the program prove defective, you assume the cost of all necessary servicing, repair or correction.
*
* Need a consultation, or assistance to customise this code? Find us on Codeable: https://aelia.co/hire_us
*/
/**
* Retrieves the cart contents. We can't just call WC_Cart::get_cart(), because
* such method runs multiple actions and filters, which we don't want to trigger
* at this stage.
*
* @author Aelia <support@aelia.co>
*/
function aelia_get_cart_contents() {
$cart_contents = array();
/**
* Load the cart object. This defaults to the persistant cart if null.
*/
$cart = WC()->session->get( 'cart', null );
if ( is_null( $cart ) && ( $saved_cart = get_user_meta( get_current_user_id(), '_woocommerce_persistent_cart_' . get_current_blog_id(), true ) ) ) { // @codingStandardsIgnoreLine
$cart = $saved_cart['cart'];
}
elseif ( is_null( $cart ) ) {
$cart = array();
}
elseif ( is_array( $cart ) && ( $saved_cart = get_user_meta( get_current_user_id(), '_woocommerce_persistent_cart_' . get_current_blog_id(), true ) ) ) { // @codingStandardsIgnoreLine
$cart = array_merge( $saved_cart['cart'], $cart );
}
if ( is_array( $cart ) ) {
foreach ( $cart as $key => $values ) {
$_product = wc_get_product( $values['variation_id'] ? $values['variation_id'] : $values['product_id'] );
if ( ! empty( $_product ) && $_product->exists() && $values['quantity'] > 0 ) {
if ( $_product->is_purchasable() ) {
// Put session data into array. Run through filter so other plugins can load their own session data
$session_data = array_merge( $values, array( 'data' => $_product ) );
$cart_contents[ $key ] = apply_filters( 'woocommerce_get_cart_item_from_session', $session_data, $values, $key );
}
}
}
}
return $cart_contents;
}
// Step 1 - Keep track of cart contents
add_action('wp_loaded', function() {
// If there is no session, then we don't have a cart and we should not take
// any action
if(!is_object(WC()->session)) {
return;
}
// This variable must be global, we will need it later. If this code were
// packaged as a plugin, a property could be used instead
global $allowed_cart_items;
// We decided that products with ID 737 and 832 can go together. If any of them
// is in the cart, all other products cannot be added to it
global $restricted_cart_items;
$restricted_cart_items = array(
// Set the IDs of the products that can be added to the cart at the same time
1,
2,
3,
);
// "Snoop" into the cart contents, without actually loading the whole cart
foreach(aelia_get_cart_contents() as $item) {
if(in_array($item['data']->get_id(), $restricted_cart_items)) {
$allowed_cart_items[] = $item['data']->get_id();
// If you need to allow MULTIPLE restricted items in the cart, comment
// the line below
break;
}
}
// Step 2 - Make disallowed products "not purchasable"
add_filter('woocommerce_is_purchasable', function($is_purchasable, $product) {
global $restricted_cart_items;
global $allowed_cart_items;
// If any of the restricted products is in the cart, any other must be made
// "not purchasable"
if(!empty($allowed_cart_items)) {
// To allow MULTIPLE products from the restricted ones, use the line below
//$is_purchasable = in_array($product->id, $allowed_cart_items) || in_array($product->id, $restricted_cart_items);
// To allow a SINGLE products from the restricted ones, use the line below
$is_purchasable = in_array($product->get_id(), $allowed_cart_items);
}
return $is_purchasable;
}, 10, 2);
}, 10);
// Step 3 - Explain customers why they can't add some products to the cart
add_filter('woocommerce_get_price_html', function($price_html, $product) {
if(!$product->is_purchasable() && is_product()) {
$price_html .= '<p>' . __('This product cannot be purchased together with "Product X" or "Product Y". If you wish to buy this product, please remove the other products from the cart.', 'woocommerce') . '</p>';
}
return $price_html;
}, 10, 2);
@paguja
Copy link

paguja commented May 22, 2020

Hi,
Thanks, I've been looking for this for a long time and it works well.
In step 3, I would like the message to be translated with Polylang. Do you know how to do it ?

@daigo75
Copy link
Author

daigo75 commented May 22, 2020

The message is passed to the __() localisation function. Polylang should be able to translate it using Polylang's string translation. I'm not familiar with that specific plugin, though, I would recommend to ask its team how to proceed.

@paguja
Copy link

paguja commented May 22, 2020

Ok thank you. I'm going to ask Polylang if they can help me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment