Search code examples
phpwordpresswoocommercecartshipping-method

Offer free shipping if user purchases "x" products from a single category


I currently charge a flat fee for shipping using the default WooCommerce shipping setup. I want to offer free shipping for the entire order if the user purchases "x" products from a single category. I have some code that I put together but I need a bit of help to get it working.

// Free shipping if you purchase 12 or more from selected category
function wcs_my_free_shipping( $is_available ) {
    global $woocommerce;

    // HERE set your product categories in the array (can be IDs, slugs or names)
    $categories = array('t-shirts');

    // Initializing
    $found = false;
    $count = 0;

     // 1st Loop: get category items count  
    foreach ( WC()->cart->get_cart() as $cart_item ) {
        // If product categories is found
        if ( has_term( $categories, 'product_cat', $cart_item['product_id'] ) ) {
            $count += $cart_item['quantity'];
        }
    }

    // get cart contents
    $cart_items = $woocommerce->cart->get_cart();

    // loop through the items looking for one in the eligible array
    foreach ( $cart_items as $key => $item ) {
        if( in_array( $item['product_id'], $eligible ) ) {
            return true;
        }
    }

    if ( $count > 11 ) {
        // Apply free shipping
        $shipping = 0;
    }
}
add_filter( 'woocommerce_shipping_free_shipping_is_available', 'wcs_my_free_shipping', 20 );

Solution

    • The use of 2 loops is not necessary
    • Take into account the product quantity per product
    • comment with explanation added in the code
    function filter_woocommerce_shipping_free_shipping_is_available( $is_available, $package, $shipping_method ) {
        // Set categories
        $categories = array ( 't-shirts' );
        
        // Set minimum
        $minimum = 12;
        
        /* END settings */
    
        // Counter
        $count = 0;
        
        // Loop through cart items
        foreach( $package['contents'] as $cart_item ) {
            // If product categories is found
            if ( has_term( $categories, 'product_cat', $cart_item['product_id'] ) ) {
                $count += $cart_item['quantity'];
            }
        }
        
        // Condition
        if ( $count >= $minimum ) {
            $notice = __( 'free shipping', 'woocommerce' );
            $is_available = true;   
        } else {
            $notice = __( 'NO free shipping', 'woocommerce' );
            $is_available = false;      
        }
        
        // Display notice
        if ( isset( $notice ) ) {
            wc_add_notice( $notice, 'notice' );
        }
     
        // Return
        return $is_available;
    }
    add_filter( 'woocommerce_shipping_free_shipping_is_available', 'filter_woocommerce_shipping_free_shipping_is_available', 10, 3 );
    

    • Additional question: hide other shipping methods when free shipping is available
    function filter_woocommerce_package_rates( $rates, $package ) { 
        // Set categories
        $categories = array ( 't-shirts' );
        
        // Set minimum
        $minimum = 11;
        
        /* END settings */
    
        // Counter
        $count = 0;
    
        // Loop through line items
        foreach( $package['contents'] as $line_item ) {
            // Get product id
            $product_id = $line_item['product_id'];
    
            // Check for category
            if ( has_term( $categories, 'product_cat', $product_id ) ) {
                $count += $line_item['quantity'];
            }
        }
    
        // Condition
        if ( $count > $minimum ) {
            // Set
            $free = array();
        
            // Loop
            foreach ( $rates as $rate_id => $rate ) {
                // Rate method id = free shipping
                if ( $rate->method_id === 'free_shipping' ) {
                    $free[ $rate_id ] = $rate;
                    break;
                }
            }
        }
        
        return ! empty( $free ) ? $free : $rates;
    }
    add_filter( 'woocommerce_package_rates', 'filter_woocommerce_package_rates', 10, 2 );
    

    Based on: