Allow checkout only when a product of a mandatory

2019-01-20 12:41发布

问题:

I'd like to stop any customer advancing to the checkout if they do not have a particular product category in their basket. I would also like to tell them with an error message that they need to add a certain product. I've found some code but cannot it to work. I've added it as a code snippet into my Wordpress install but alas it does not function and there are no error messages even though I have debugging switched on. Here is the code that I have found in Github that may need modification in order for this to work:

function sv_wc_prevent_checkout_for_category() {

    // set the slug of the category for which we disallow checkout
    $category = 'sibling';

    // 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;
}

So I'm looking to stop checkout (with error message) if the 'sibling' category is the only item in the cart. I have a 'standard' category which must be in the basket before the customer makes it to the checkout. Hope this makes sense.

回答1:

Here you have a solution that will make the trick. There is especially 2 main functions (the last ones):

  1. The first function (N°3) display your message on cart page, when there is something in cart but not the mandatory product category. Displays also the message on the mandatory product archive pages (useful when customer get redirected from checkout, see below).
  2. The second function (N°4) redirect customer to the product mandatory category archive pages when it tries to checkout and his cart has not that missing mandatory product category.

Define before your mandatory category slug in your_mandatory_category_slug() function.

This is the code:

// Function that define the mandatory product category
 function your_mandatory_category_slug(){

     // DEFINE HERE the SLUG of the needed product category
    $category = 'clothing';
    return $category;
 }


// Conditional function that returns true if the mandatory product category is in cart
function has_mandatory_category(){
    $category_needed = your_mandatory_category_slug();
    $has_cat = false;

    // Iterrating each item in cart and detecting…
    foreach ( WC()->cart->get_cart() as $item ) {

        // Detects if the needed product category is in cart items
        if ( has_term($category_needed, 'product_cat', $item['product_id'] ) ) {
            $has_cat = true;
            break;
        }
    }
    return $has_cat;
 }


// Function that display a message if there is not in cart a mandatory product category
function mandatory_category_display_message() {
        $category_needed = your_mandatory_category_slug();

    // check that cart is not empty (for cart and product category archives)
    if( !WC()->cart->is_empty() && ( is_cart() || is_product_category( $category_needed ) ) ){
        $category_obj = get_term_by( 'slug', $category_needed, 'product_cat' );
        if ( is_wp_error( $category_obj ) ) return;

        // Display message when product category is not in cart items
        if ( !has_mandatory_category() ) {
            $category_name = $category_obj->name;
            $category_url = get_term_link( $category_needed, 'product_cat' );

            // render a notice to explain why checkout is blocked
            wc_add_notice( sprintf( __( '<strong>Reminder:</strong> You have to add in your cart, a product from "%1$s" category, to be allowed to check out. Please return <a href="%2$s"> here to "%1$s" product page</a>', 'your_theme_domain'), $category_name, $category_url ), 'error' );
        }
    }
}
add_action( 'woocommerce_before_main_content', 'mandatory_category_display_message', 30 ); // for product mandatory category archives pages
add_action( 'woocommerce_check_cart_items', 'mandatory_category_display_message' ); // for cat page


// Function that redirect from checkout to mandatory product category archives pages
function mandatory_category_checkout_redirect() {

    // If cart is not empty on checkout page
    if( !WC()->cart->is_empty() && is_checkout() ){
        $category_needed = your_mandatory_category_slug();

        // If missing product category => redirect to the products category page
        if ( !has_mandatory_category() )
            wp_redirect( get_term_link( $category_needed, 'product_cat' ) );
    }
}
add_action('template_redirect', 'mandatory_category_checkout_redirect');

This goes in function.php file of your active child theme (or theme) or also in any plugin file.

This code is tested and fully functional.



回答2:

Works like a charm for my Wordpress installation with Woocommerce v2.6.12.

I would like to add two mandatory product-categories, maybe 3 or 4 in the future. I tried changing:

$category = 'slug1';

to

$category = 'slug1', 'slug2';

and

$category = array('slug1','slug2');

Without success. Is there a way to make this code work with multiple slugs?