Skip to content

Instantly share code, notes, and snippets.

@mikejolley
Last active May 17, 2021 13:51
Show Gist options
  • Save mikejolley/347b8f162257f6736c4d to your computer and use it in GitHub Desktop.
Save mikejolley/347b8f162257f6736c4d to your computer and use it in GitHub Desktop.
WooCommerce - Split shipping class items into a new package and limit shipping methods
/**
* This function loops over cart items, and moves any item with shipping class 'special-class' into a new package.
* The new package in this example only takes flat rate shipping.
*/
function split_special_shipping_class_items( $packages ) {
$found_item = false;
$special_class = 'special-class'; // edit this with the slug of your shippig class
$new_package = current( $packages );
$new_package['contents'] = array();
$new_package['contents_cost'] = 0;
$new_package['applied_coupons'] = array();
$new_package['ship_via'] = array( 'flat_rate' ); // Only allow flat rate for items in special class
foreach ( WC()->cart->get_cart() as $item_key => $item ) {
// Is the product in the special class?
if ( $item['data']->needs_shipping() && $special_class === $item['data']->get_shipping_class() ) {
$found_item = true;
$new_package['contents'][ $item_key ] = $item;
$new_package['contents_cost'] += $item['line_total'];
// Remove from original package
$packages[0]['contents_cost'] = $packages[0]['contents_cost'] - $item['line_total'];
unset( $packages[0]['contents'][ $item_key ] );
// If there are no items left in the previous package, remove it completely.
if ( empty( $packages[0]['contents'] ) ) {
unset( $packages[0] );
}
}
}
if ( $found_item ) {
$packages[] = $new_package;
}
return $packages;
}
// Hook into shipping packages filter
add_filter( 'woocommerce_cart_shipping_packages', 'split_special_shipping_class_items' );
@Crytix
Copy link

Crytix commented Sep 4, 2019

@progbdb and @JimmyAppelt even if the answer comes quite late. Maybe some people are still interested if you stumble across this article. I use the function of @mikejolley and have added some more functions and overrides.

Here I create the new package for "local_pickup".

/**
 * This function loops over cart items, and moves any item with shipping class 'special-class' into a new package. 
 * The new package only takes local pickup.
 */
function split_special_shipping_class_items( $packages ) {
	$found_item                     = false;
	$special_class                  = 'special-class'; // edit this with the slug of your local pickup shippig class
	$new_package                    = current( $packages );
	$new_package['contents']        = array();
	$new_package['contents_cost']   = 0;
	$new_package['applied_coupons'] = array();
	$new_package['ship_via']        = array( 'local_pickup' ); // Only allow local pickup for items in special class
	
	foreach ( WC()->cart->get_cart() as $item_key => $item ) {
		// Is the product in the special class?
		if ( $item['data']->needs_shipping() && $special_class === $item['data']->get_shipping_class() ) {
			$new_package['name'] = 'PickUp'; // your custom label for this package
			$found_item                            = true;
			$new_package['contents'][ $item_key ]  = $item;
			$new_package['contents_cost']         += $item['line_total'];
			// Remove from original package
			$packages[0]['contents_cost'] = $packages[0]['contents_cost'] - $item['line_total'];
			unset( $packages[0]['contents'][ $item_key ] );
			// If there are no items left in the previous package, remove it completely.
			if ( empty( $packages[0]['contents'] ) ) {
				unset( $packages[0] );
			}
		}
	}
	if ( $found_item ) {
	   $packages[] = $new_package;
	}
	return $packages;
}

function rename_custom_package( $package_name, $i, $package ) {
 
    if ( ! empty( $package['name'] ) ) {
        $package_name = $package['name'];
    }
 
    return $package_name;
}

@TomLuijts
Hide the Local PickUp option for the package being shipped.

function hide_shipping_method_based_on_shipping_class( $rates, $package )
{
    if ( is_admin() && ! defined( 'DOING_AJAX' ) )
        return;
    // HERE define your shipping class to find
    $class = 77;
    // HERE define the shipping methods you want to hide
    $method_key_ids = array('local_pickup:8', 'local_pickup:9');
    // Checking in cart items
    foreach( $package['contents'] as $item ) {
        // If we find the shipping class
        if( $item['data']->get_shipping_class_id() == $class ){
            foreach( $method_key_ids as $method_key_id ){
                unset($rates[$method_key_id]); // Remove the targeted methods
            }
            break; // Stop the loop
        }
    }
    return $rates;
}

The following lines must be added so that the changes can also be displayed.

// hook into shipping packages filter
add_filter( 'woocommerce_cart_shipping_packages', 'split_special_shipping_class_items' );
// output the new package name
add_filter( 'woocommerce_shipping_package_name', 'rename_custom_package', 10, 3 );
// hide local pickup on dropshipping items
add_filter( 'woocommerce_package_rates', 'hide_shipping_method_based_on_shipping_class', 10, 2 );

I myself have specified the delivery time and have a message displayed during the checkout, which refers to the Local PickUp articles.

Thanks again to all who contributed directly and indirectly to the code like @dvelopit and @mikejolley .

A picture as it looks like and lines of code you find in my git https://github.com/Crytix/Woocommerce-Local-PickUp-and-Shipping-Split

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment