Search code examples
phpwordpresswoocommercecustom-taxonomyshipping-method

Hide shipping methods based on products categories in Woocommerce


With Woocommerce, I would like to hide all shipping methods except "Local pickup" when a defined products category is in cart…

The code below does that for other product types, except variable products:

add_filter( 'woocommerce_package_rates', 'custom_shipping_methods', 100, 2 );
function custom_shipping_methods( $rates, $package ){

    // Define/replace here your correct category slug (!)
    $cat_slug = 'my_category_slug';
    $prod_cat = false;

    // Going through each item in cart to see if there is anyone of your category
    foreach ( WC()->cart->get_cart() as $values ) {
        $product = $values['data'];

        // compatibility with WC +3
        $product_id = method_exists( $product, 'get_id' ) ? $product->get_id() : $product->id;

        if ( has_term( $cat_slug, 'product_cat', $product_id ) )
            $prod_cat = true;
    }

    $rates_arr = array();

    if ( $prod_cat ) {
        foreach($rates as $rate_id => $rate) { // <== There was a mistake here
            if ('local_pickup' === $rate->method_id) {
                $rates_arr[ $rate_id ] = $rate;
            }
        }
    }
    return !empty( $rates_arr ) ? $rates_arr : $rates;
}

What can I do to make it work for variable products too? Any help is appreciated.


Solution

  • I have revisited your code and here is correct way to make it work for variable products too:

    add_filter( 'woocommerce_package_rates', 'product_category_hide_shipping_methods', 90, 2 );
    function product_category_hide_shipping_methods( $rates, $package ){
    
        // HERE set your product categories in the array (IDs, slugs or names)
        $categories = array( 'clothing');
        $found = false;
    
        // Loop through each cart item Checking for the defined product categories
        foreach( $package['contents'] as $cart_item ) {
            if ( has_term( $categories, 'product_cat', $cart_item['product_id'] ) ){
                $found = true;
                break;
            }
        }
    
        $rates_arr = array();
        if ( $found ) {
            foreach($rates as $rate_id => $rate) { 
                if ('local_pickup' === $rate->method_id) {
                    $rates_arr[ $rate_id ] = $rate;
                }
            }
        }
        return !empty( $rates_arr ) ? $rates_arr : $rates;
    }
    

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

    You should need to refresh the shipping caches:
    1) First this code is already saved on your function.php file.
    2) In Shipping settings, enter in a Shipping Zone and disable a Shipping Method and "save". Then re-enable that Shipping Method and "save". You are done.