Created
July 23, 2014 05:40
-
-
Save kane-c/5e4d53601564ed667fb9 to your computer and use it in GitHub Desktop.
WordPress WooCommerce: Force local pickup as the only shipping option when products in the cart require it.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// Force pickup as a shipping option if one or more products in the catalog is marked as pickup only. | |
// To do this, add a shipping class with the slug 'pickup-only' then set products with that class as required. | |
// Add this script to your theme's functions.php or similar. | |
function hideShippingWhenPickupRequired($rates, $package) | |
{ | |
foreach ($package['contents'] as $item) { | |
$product = $item['data']; | |
$shippingClass = $product->get_shipping_class(); | |
if ('pickup-only' === $shippingClass) { | |
// The cart requires pickup | |
return array( | |
'local_pickup' => $rates['local_pickup'], | |
); | |
} | |
} | |
return $rates; | |
} | |
add_filter('woocommerce_package_rates', 'hideShippingWhenPickupRequired', 10, 2); |
I expanded the above snippets with an extra shipping class for products that are not delivered separately.
Tested with Wordpress 5.7.1 and WooCommerce 5.2.2
/**
* Hide all other methods if:
* - At least one item has shipping class 'local-pickup-only'
* OR
* - All items have shipping class 'no-individual-delivery'
**/
function check_local_pickup_only( $rates, $package ) {
$localPickupOnly = false;
$noIndividualDelivery = false;
$regularDelivery = false;
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$product = $cart_item['data'];
$class = $product->get_shipping_class();
switch ($class) {
case 'local-pickup-only':
$localPickupOnly = true;
break 2;
case 'no-individual-delivery':
$noIndividualDelivery = true;
break;
default:
$regularDelivery = true;
break;
}
}
if ( $noIndividualDelivery && !$regularDelivery ) {$localPickupOnly = true;}
$local = array();
foreach ( $rates as $rate_id => $rate ) {
if ('local_pickup' === $rate->method_id ) {
$local[ $rate_id ] = $rate;
}
}
if ( !empty($local) && ($localPickupOnly == true) ) {
return $local;
} else {
return $rates;
}
}
add_filter( 'woocommerce_package_rates', 'check_local_pickup_only', 10, 2 );
Here's my solution: for carts with one or more products belonging to the shipping class "Local Pickup Only" ($shipping_class_name = 'local-pickup-only'
), only the local pickup location(s) (from the Local Pickup in Blocks) will appear.
Tested with WordPress 6.5.2 and WooCommerce 8.7.0.
Important: "Clear customer sessions" after implementation (WooCommerce > Status > Tools > Clear customer sessions).
// WooCommerce display only "Local pickup" location(s) (from new "WooCommerce Blocks: Local Pickup" - Settings > Shipment > "Local Pickup" tab - https://woocommerce.com/document/woocommerce-blocks-local-pickup/) if one or more products added to the cart belong to the $shipping_class_name = 'local-pickup-only', dynamically unsets all shipping methods except those with values starting with "pickup_location:".
// Please note that the new "WooCommerce Blocks: Local Pickup" does not work with Elementor as it requires the Checkout block to enable it (as described here https://woocommerce.com/document/using-the-new-block-based-checkout/).
add_filter($hook_name='woocommerce_package_rates', $callback='woocommerce_shipping_method_local_pickup_only', $priority=10, $accepted_args=1);
function woocommerce_shipping_method_local_pickup_only( $rates ) {
if ( WC() ) {
// Setup
$shipping_class_name = 'local-pickup-only';
$in_cart = false;
foreach ( WC()->cart->get_cart_contents() as $key => $values ) {
if ( $values['data']->get_shipping_class() === $shipping_class_name ) {
$in_cart = true;
break;
}
}
if ( $in_cart ) {
// Unset all shipping methods except for the ones with value starting with "pickup_location:"
foreach ( $rates as $rate_key => $rate ) {
if ( strpos( $rate_key, 'pickup_location:' ) !== 0 ) {
unset( $rates[ $rate_key ] );
}
}
}
return $rates;
}
}
For those using the "legacy" local pickup, the code below accomplishes the same goal.
// WooCommerce display only chosen $local_pickup_ids if one or more products added to the cart belong to the $shipping_class_name = 'local-pickup-only', dynamically unsets all shipping methods except for chosen $local_pickup_ids.
add_filter($hook_name='woocommerce_package_rates', $callback='woocommerce_shipping_method_local_pickup_only', $priority=10, $accepted_args=1);
function woocommerce_shipping_method_local_pickup_only( $rates ) {
if ( WC() ) {
// Setup
$shipping_class_name = 'local-pickup-only';
$local_pickup_ids = array('local_pickup:38');
$in_cart = false;
foreach ( WC()->cart->get_cart_contents() as $key => $values ) {
if ( $values['data']->get_shipping_class() === $shipping_class_name ) {
$in_cart = true;
break;
}
}
if ( $in_cart ) {
// Unset all shipping methods except for $local_pickup_ids
foreach ( $rates as $rate_key => $rate ) {
if ( !in_array($rate_key, $local_pickup_ids) ) {
unset( $rates[$rate_key] );
}
}
}
return $rates;
}
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi
Snippets above doesn't work for me, but I found this working solution: WooCommerce: Hide Shipping Rate if Shipping Class @ Cart
Clear transients after implementation!