Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save bekarice/99d479e6c91880b3b80a21a0b3415b7f to your computer and use it in GitHub Desktop.
Save bekarice/99d479e6c91880b3b80a21a0b3415b7f to your computer and use it in GitHub Desktop.
Prevents checkout if the WooCommerce cart only contains items from a specific category
<?php // only copy this line if needed
/**
* Renders a notice and prevents checkout if the cart
* only contains products in a specific category
*/
function sv_wc_prevent_checkout_for_category() {
// set the slug of the category for which we disallow checkout
$category = 'clothing';
// get the product category
$product_cat = get_term_by( 'slug', $category, 'product_cat' );
// sanity check to prevent fatals if the term doesn't exist
if ( is_wp_error( $product_cat ) ) {
return;
}
$category_name = '<a href="' . get_term_link( $category, 'product_cat' ) . '">' . $product_cat->name . '</a>';
// check if this category is the only thing in the cart
if ( sv_wc_is_category_alone_in_cart( $category ) ) {
// render a notice to explain why checkout is blocked
wc_add_notice( sprintf( 'Hi there! Looks like your cart only contains products from the %1$s category &ndash; you must purchase a product from another category to check out.', $category_name ), 'error' );
}
}
add_action( 'woocommerce_check_cart_items', 'sv_wc_prevent_checkout_for_category' );
/**
* Checks if a cart contains exclusively products in a given category
*
* @param string $category the slug of the product category
* @return bool - true if the cart only contains the given category
*/
function sv_wc_is_category_alone_in_cart( $category ) {
// check each cart item for our category
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
// if a product is not in our category, bail out since we know the category is not alone
if ( ! has_term( $category, 'product_cat', $cart_item['data']->id ) ) {
return false;
}
}
// if we're here, all items in the cart are in our category
return true;
}
@twigling
Copy link

Thank you for your input. I have disabled the original snippet and set this one instead. I cannot tell you if it works, because now both cart and checkout load with everything blank below the page title... so I can't even empty the cart. For the sake of checking, I changed the theme of my site to twenty-fourteen but that didn't do anything...

@indextwo
Copy link

@twigling You should still use the original snippet, but replace the sv_wc_prevent_checkout_for_category with the one I gave you - there are function calls in there, so if you're removed those functions, it's likely to error out.

  1. Take the original snippet;
  2. Delete lines 7-29;
  3. Paste in the sv_wc_prevent_checkout_for_category with the add_action line from my previous comment

@twigling
Copy link

Yesssssssss, that works! Thank you so much for your help, patience and explanations 👯‍♂️ :D

@Pami72
Copy link

Pami72 commented Apr 4, 2018

Ok So I tried to use this in my cart and It didnt work... can someone Help me please....

@Pami72
Copy link

Pami72 commented Apr 4, 2018

can someone help me to get this to work... I am having issues
Thanks!

@vtsara
Copy link

vtsara commented May 22, 2018

Wanting to alter this a bit so that I can prevent checkout if the cart contains a certain combination of categories. So, I need to check to see if $category I've set AND any other category are present, if yes then prevent checkout. Or, only allow checkout if $category I set is either alone, or not present at all. The part that's tripping me up is looking for a specific category plus "any other category" ...any suggestions on this part?

Currently have this which is working almost as intended - works when catering category is alone or is combined w/ other items in cart, but still giving the error when non-catering items are alone in the cart:

// check if category is the only thing in the cart if ( lg_wc_is_category_alone_in_cart( $category ) ) { // if yes then escape this because it's all good return; } else { // otherwise post a notice to explain why checkout is blocked wc_add_notice( sprintf( 'hi there! looks like your cart contains products from the %1$s and <a href="#">pickup</a> categories &ndash; catering items must be ordered separately from pickup and shipping items to check out.', $category_name ), 'error' ); }

I feel like I'm close?

@Nodws
Copy link

Nodws commented Feb 21, 2019

This does not "prevent" checkout just escapes a notice, add exit(); to prevent lol

@theRealRizeo
Copy link

theRealRizeo commented Apr 10, 2019

To make sure the notice shows and sticks: (Updated code)

function sv_wc_prevent_checkout_for_category() {
	//	If the cart is empty, then let's hit the ejector seat
	
	if (WC()->cart->is_empty()) {
		return;
	}		

	// set the slug of the category for which we disallow checkout
	$category = 'clothing';
	
	// get the product category
	$product_cat = get_term_by( 'slug', $category, 'product_cat' );
	
	// sanity check to prevent fatals if the term doesn't exist
	if ( is_wp_error( $product_cat ) ) {
		return;
	}
	
	$category_name = '<a href="' . get_term_link( $category, 'product_cat' ) . '">' . $product_cat->name . '</a>';
	
	// check if this category is the only thing in the cart
	if ( sv_wc_is_category_alone_in_cart( $category ) ) {
		
		// render a notice to explain why checkout is blocked
		wc_add_notice( sprintf( 'Hi there! Looks like your cart only contains products from the %1$s category &ndash; you must purchase a product from another category to check out.', $category_name ), 'error' );
return false;
	}
return true;
}

add_action( 'woocommerce_check_cart_items', 'sv_wc_prevent_checkout_for_category' );

@theRealRizeo
Copy link

You need to return true or false. false tells WooCommerce there is an error and picks your notice

@rtpHarry
Copy link

rtpHarry commented Dec 9, 2019

Thanks for this!

For future readers: I was hunting around trying to figure this out and this thread helped but it seems that it's not the return false or true that blocks the checkout.

It's the wp_add_notice with an error parameter that does it:

		// Check cart contents for errors.
		do_action( 'woocommerce_check_cart_items' );
		// Calc totals.
		WC()->cart->calculate_totals();
		// Get checkout object.
		$checkout = WC()->checkout();
		if ( empty( $_POST ) && wc_notice_count( 'error' ) > 0 ) { // WPCS: input var ok, CSRF ok.
			wc_get_template( 'checkout/cart-errors.php', array( 'checkout' => $checkout ) );
			wc_clear_notices();
		} else {

You can see it doesn't do anything but called the woocommerce_check_cart_items action.

The bit that sends it to the blocked checkout page is finding wc_notice_count( 'error' ) > 0.

@rtpHarry
Copy link

rtpHarry commented Dec 9, 2019

Bonus tip: I wanted to inject my own product configuration page in as the checkout error page.

To this you can do two things:

  1. Use is_checkout() in your woocommerce_check_cart_items handler. This way you can add an error to block the checkout, but not actually display it anywhere.
  2. Override the template for the checkout error page to show your own configuration page.

Code would look something like this:

        /**
         * Configure the action and filter hooks
         * 
         * @since   1.0.0
         */
        public function plugin_init()
        {
            add_action('woocommerce_check_cart_items', array(&$this, 'woocommerce_check_cart_items_valid'));
            add_filter('wc_get_template', array(&$this, 'woocommerce_template_override'), 10, 5);
        }

        /**
         * Halt the checkout if cart contains unconfigured products
         * 
         * @since 1.0.0
         */
        public function woocommerce_check_cart_items_valid()
        {
            // Only run in the Checkout page for the reason explained below
            if (is_checkout()) {
                if (!$this->is_cart_valid()) {
                    // must add an error notice otherwise it won't block the checkout.
                    // this won't actually be seen anywhere as its only applied on the checkout
                    // page and checkout redirects to the cart errors page instead
                    wc_add_notice(
                        __('You must configure your products before you can checkout.', 'textdomain-here'),
                        'error'
                    );
                }
            }
        }

        /**
         * Override the checkout error page if the cart contains unconfigured products
         * 
         * @since 1.0.0
         */
        public function woocommerce_template_override($located, $template_name, $args, $template_path, $default_path)
        {
            if ('checkout/cart-errors.php' == $template_name) {
                if (!$this->is_cart_valid()) {
                    $located = MY_PLUGIN_DIR . 'templates/checkout/configure-products.php';
                }
            }

            return $located;
        }

@DamianKardas
Copy link

Hi, it's there a way to make this for two or more categories? so If customer have in cart something from Category A and from Category B then will see message and not allow to go to checkout? or if got Category B and Category C also, or Cat A + Cat B + Cat C?

@ArneSaknussemm
Copy link

Bonus tip: I wanted to inject my own product configuration page in as the checkout error page.

To this you can do two things:

1. Use `is_checkout()` in your `woocommerce_check_cart_items` handler. This way you can add an error to block the checkout, but not actually display it anywhere.

2. Override the template for the checkout error page to show your own configuration page.

Code would look something like this:

        /**
         * Configure the action and filter hooks
         * 
         * @since   1.0.0
         */
        public function plugin_init()
        {
            add_action('woocommerce_check_cart_items', array(&$this, 'woocommerce_check_cart_items_valid'));
            add_filter('wc_get_template', array(&$this, 'woocommerce_template_override'), 10, 5);
        }

        /**
         * Halt the checkout if cart contains unconfigured products
         * 
         * @since 1.0.0
         */
        public function woocommerce_check_cart_items_valid()
        {
            // Only run in the Checkout page for the reason explained below
            if (is_checkout()) {
                if (!$this->is_cart_valid()) {
                    // must add an error notice otherwise it won't block the checkout.
                    // this won't actually be seen anywhere as its only applied on the checkout
                    // page and checkout redirects to the cart errors page instead
                    wc_add_notice(
                        __('You must configure your products before you can checkout.', 'textdomain-here'),
                        'error'
                    );
                }
            }
        }

        /**
         * Override the checkout error page if the cart contains unconfigured products
         * 
         * @since 1.0.0
         */
        public function woocommerce_template_override($located, $template_name, $args, $template_path, $default_path)
        {
            if ('checkout/cart-errors.php' == $template_name) {
                if (!$this->is_cart_valid()) {
                    $located = MY_PLUGIN_DIR . 'templates/checkout/configure-products.php';
                }
            }

            return $located;
        }

Great, this cart checking is so much better than https://docs.woocommerce.com/document/minimum-order-amount/

@abdigital
Copy link

abdigital commented Nov 23, 2021

I'm trying to figure out how can we post a message and disable place order when a specific product from a list of 6 products IDs is not added to cart. Any ideas? Thank you

@nafanoul
Copy link

If i want to change the behavior at checkout as seen on line 22-27, how do I do that? I want to redirect to a different URL. I have tried replacing 22-27 with;
if ( sv_wc_is_category_alone_in_cart( $category ) ) {

	// render a notice to explain why checkout is blocked
	wp_redirect( 'example.com' );
		exit();
	
}

However it doesnt seem to work. Im new at this so any help would be appreciated

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