-
-
Save bekarice/99d479e6c91880b3b80a21a0b3415b7f to your computer and use it in GitHub Desktop.
<?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 – 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; | |
} |
Thank you for taking the time to respond :)!!
I feel mildly embarrassed to admit this, but I can't quite wrap my head around the concept of wrapping the conditional around the function, possibly because the sv_wc_is_category_alone_in_cart() comes up twice (but that is really just a part of my failure to comprehend, code is not my forté) could I ask you to please post an example of the entire code above, as it should be with the conditional in the correct place??
Pasting the snippet anywhere I believe it might fit, returns "Call to a member function is_empty() on null" for the line the code is in... I get that I am doing something incorrectly, but I am not getting enough information from the error message to be able to correct it.
ETA: After subconsciously dwelling on this for some hours, I think I might have had a lightbulb... will test my theory after dinner.
ETA2: Nope, whatever I did, it wasn't right.
To be more specific, when the cart is empty, it throws up both the usual "cart is empty" message AND the "you only have products from X category in your cart". I am making an uneducated guess in placing everything from line 38 down into the spot marked //all the code (also tried with the whole snippet in that spot), and get the same error message as before.
I also tried putting it in the second closed bracket after ->is empty and while it gives a different error, its clearly not right..
Thus, can someone please (pretty, with sugar on it) take a look and clarify where I should put what to make it work, or test the combination to see if it can indeed work as intended?
Hey @twigling
Looking at my code, it probably makes more sense to bail within the action
rather than sv_wc_is_category_alone_in_cart()
. So try this instead:
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 – 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' );
That should behave the way you want: if the cart is empty it will stop trying to process anything else, so you should never get that message.
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...
@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.
- Take the original snippet;
- Delete lines 7-29;
- Paste in the
sv_wc_prevent_checkout_for_category
with theadd_action
line from my previous comment
Yesssssssss, that works! Thank you so much for your help, patience and explanations 👯♂️ :D
Ok So I tried to use this in my cart and It didnt work... can someone Help me please....
can someone help me to get this to work... I am having issues
Thanks!
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 – catering items must be ordered separately from pickup and shipping items to check out.', $category_name ), 'error' ); }
I feel like I'm close?
This does not "prevent" checkout just escapes a notice, add exit();
to prevent lol
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 – 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' );
You need to return true
or false
. false
tells WooCommerce there is an error and picks your notice
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
.
Bonus tip: I wanted to inject my own product configuration page in as the checkout error page.
To this you can do two things:
- Use
is_checkout()
in yourwoocommerce_check_cart_items
handler. This way you can add an error to block the checkout, but not actually display it anywhere. - 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;
}
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?
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/
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
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
@twigling If you wrap a conditional around everything in the
sv_wc_is_category_alone_in_cart()
function:I haven't tested this, but it should work as expected.