-
-
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; | |
} |
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
This does not "prevent" checkout just escapes a notice, add
exit();
to prevent lol