Search code examples
phpwordpresswoocommercecartcategories

Deny checkout for specific cart items in Woocommerce based on product categories


Based on Allow checkout only when a product of a mandatory category is in cart I tried to make my own code sample that Renders a notice and prevents checkout if the cart only contains products in specific categories. It works on prevention and error notice however on adding other products, it still denies checkout.

/**
 * Renders a notice and prevents checkout if the cart
 * only contains products in a specific category
 */
//add_action( 'woocommerce_check_cart_items', 'brown_wc_prevent_checkout_for_category' );

function brown_wc_prevent_checkout_for_category() {

    // set the slug of the category for which we disallow checkout
    $categories = array('drinks','extra-accompaniments');

    foreach ($categories as $category => $value) {
        // 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;
        }

        // check if this category is the only thing in the cart
        if ( brown_wc_is_category_alone_in_cart( $category ) ) {

            // render a notice to explain why checkout is blocked
            wc_add_notice( sprintf( 'Please choose atleast one bento.', $category ), 'error' );
        }
    }

}

/**
 * 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 brown_wc_is_category_alone_in_cart( $category ) {

    //When the cart is empty, remove warning message that I have set for when the snippet takes effect
    if (!WC()->cart->is_empty()) {
        // All the code
        // 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;

    } else {

        return false;   //  Assume you'd want false here, since the cart is empty

    }

}

Solution

  • There is some mistakes in your code that avoid it to work successfully. Your code and requirements are quiet different, than my previous answer.

    You will have to set your needed button URL and text for the notice, like the "bento" product category link and button name.

    Here is the code:

    // Conditional function that check if the non mandatory product categories are in all cart items
    function has_not_mandatory_category(){
        // DEFINE HERE the your non mandatory product categories (Ids, slugs or names)
        $categories = array('drinks','extra-accompaniments');
    
        // Iterrating each item in cart and detecting…
        foreach ( WC()->cart->get_cart() as $cart_item ) {
            $product_id = $cart_item['product_id']; // <== This is the right way (working for product variations too)
            if ( ! has_term( $categories, 'product_cat', $product_id ) )
                return false;
        }
        return true;
    }
    
    // Display a message and prevent checkout if the non mandatory product categories are not in all cart items
    add_action( 'woocommerce_check_cart_items', 'prevent_checkout_display_notice' ); // for cart and checkout
    function prevent_checkout_display_notice() {
        // DEFINE HERE the return button URL and title
        $button_url = '#';
        $button_title = 'Go there';
    
        // Display message if the non mandatory product categories are not in all cart items
        if ( has_not_mandatory_category() )
            wc_add_notice(
                sprintf( __( 'Please choose at least one bento. <a class="button" href="%s">%s</a>', 'your_theme_domain'),
                    $button_url,
                    $button_title
                ), 'error'
            );
    }
    

    Code goes in function.php file of your active child theme (or active theme).

    Tested and works…

    Instead of targeting non mandatory product categories you should do the contrary…