WooCommerce Cart - Conditional Items categories va

2019-01-20 13:54发布

问题:

We have an exclusive category X and others regular categories Y. What I would like:

  • When someone orders anything from category X, other category items cannot be added to cart and should display a warning
  • category Y products should not be mixed with X.

How could I achieve that?

I got this code from other post, but its outdated and not satisfactory:

<?php

 // Enforce single parent category items in cart at a time based on first item in cart
function get_product_top_level_category ( $product_id ) {

    $product_terms            =  get_the_terms ( $product_id, 'product_cat' );
    $product_category         =  $product_terms[0]->parent;
    $product_category_term    =  get_term ( $product_category, 'product_cat' );
    $product_category_parent  =  $product_category_term->parent;
    $product_top_category     =  $product_category_term->term_id;

    while ( $product_category_parent  !=  0 ) {
            $product_category_term    =  get_term ( $product_category_parent, 'product_cat' );
            $product_category_parent  =  $product_category_term->parent;
            $product_top_category     =  $product_category_term->term_id;
    }

    return $product_top_category;
}

add_filter ( 'woocommerce_before_cart', 'restrict_cart_to_single_category' );
function restrict_cart_to_single_category() {
    global $woocommerce;
    $cart_contents    =  $woocommerce->cart->get_cart( );
    $cart_item_keys   =  array_keys ( $cart_contents );
    $cart_item_count  =  count ( $cart_item_keys );

    // Do nothing if the cart is empty
    // Do nothing if the cart only has one item
    if ( ! $cart_contents || $cart_item_count == 1 ) {
            return null;
    }

    // Multiple Items in cart
    $first_item                    =  $cart_item_keys[0];
    $first_item_id                 =  $cart_contents[$first_item]['product_id'];
    $first_item_top_category       =  get_product_top_level_category ( $first_item_id );
    $first_item_top_category_term  =  get_term ( $first_item_top_category, 'product_cat' );
    $first_item_top_category_name  =  $first_item_top_category_term->name;

    // Now we check each subsequent items top-level parent category
    foreach ( $cart_item_keys as $key ) {
            if ( $key  ==  $first_item ) {
                    continue;
            }
            else {
                    $product_id            =  $cart_contents[$key]['product_id'];
                    $product_top_category  =  get_product_top_level_category( $product_id );

                    if ( $product_top_category  !=  $first_item_top_category ) {
                            $woocommerce->cart->set_quantity ( $key, 0, true );
                            $mismatched_categories  =  1;
                    }
            }
    }

    // we really only want to display this message once for anyone, including those that have carts already prefilled
    if ( isset ( $mismatched_categories ) ) {
            echo '<p class="woocommerce-error">Only one category allowed in cart at a time.<br />You are currently allowed only <strong>'.$first_item_top_category_name.'</strong> items in your cart.<br />To order a different category empty your cart first.</p>';
    }
}
?>

Thanks

回答1:

Like everything is turning around your exclusive category category X, you need to use a conditional for this category.

And you have chance because there is a special function that you can use in combination with woocommerce product categories. Lets say that **cat_x** is the slug for your exclusive category, as you know it yet product_cat is the argument to get products categories.

So with has_term () conditional function, you are going to use this:

if ( has_term ('cat_x', 'product_cat', $item_id ) ) { // or $product_id
    // doing something when product item has 'cat_x'
} else {
    // doing something when product item has NOT 'cat_x'
}

We need to run the cart items 2 times in a foreach loop:

  • To detect if there is a cat_x item in that car.
  • To remove other items if cat_x is detected for one item in the cart and to fire the right messages.

In the code below, I have changed to a more useful hook. This hook will check what you have in your cart. The idea is to removed other categories items in the cart when there is a 'cat_x' item added in cart.

The code is well commented. At the end you will find different notices that are fired. You will need to put your real text in each.

add_action( 'woocommerce_check_cart_items', 'checking_cart_items' );
function checking_cart_items() {

    global $woocommerce; // if needed, not sure (?)
    $special = false;
    $catx = 'cat_x';
    $number_of_items = sizeof( WC()->cart->get_cart() );

    if ( $number_of_items > 0 ) {

        // Loop through all cart products
        foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
            $item = $values['data'];
            $item_id = $item->id;

            // detecting if 'cat_x' item is in cart
            if ( has_term( $catx, 'product_cat', $item_id ) ) {
                if (!$special) {
                    $special = true;
                }
            }
        }

        // Re-loop through all cart products
        foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
            $item = $values['data'];
            $item_id = $item->id;

            if ( $special ) // there is a 'cat_x' item in cart
            { 
                if ( $number_of_items == 1 ) { // only one 'cat_x' item in cart
                    if ( empty( $notice ) ){
                        $notice = '1';
                    }
                }
                if ( $number_of_items >= 2 ) { // 'cat_x' item + other categories items in cart

                    // removing other categories items from cart
                    if ( !has_term( $catx, 'product_cat', $item_id ) ) {
                        WC()->cart->remove_cart_item( $cart_item_key ); // removing item from cart
                        if ( empty( $notice ) || $notice == '1' ){
                            $notice = '2';
                        }
                    }
                }
            } else { // Only other categories items

                if ( empty( $notice ) ){
                    $notice = '3';
                }
            }
        }

        // Firing woocommerce notices
        if ( $notice == '1' ) { // message for an 'cat_x' item only (alone)
            wc_add_notice( sprintf( '<p class="woocommerce-error">bla bla bla one category X item in the cart</p>' ), 'success' );
        } elseif ( $notice == '2' ) { // message for an 'cat_x' item and other ones => removed other ones 
            wc_add_notice( sprintf( '<p class="woocommerce-error">bla bla bla ther is already category X in the cart => Other category items has been removed</p>' ), 'error' );
        } elseif ( $notice == '3' ) { // message for other categories items (if needed)
            wc_add_notice( sprintf( '<p class="woocommerce-error">bla bla bla NOT category X in the cart</p>' ), 'success' );
        }
    }
}       

Is not possible for me to really test this code (but it doesn't throws errors)…


@edit

We can use something else than notices… everything is possible. But it's a good starting solution, to fine tune.

You will need to replace 'cat_x' by your real category slug (in the beginning)