I am working on a WordPress site that uses WooCommerce. I found this piece of code that allows me to set a freight shipping option based on weight. It worked great until the client also wanted the same freight shipping option if a freight shipping class is selected on a product.
add_filter( 'woocommerce_package_rates', 'bbloomer_woocommerce_tiered_shipping', 10, 2 );
function bbloomer_woocommerce_tiered_shipping( $rates, $package ) {
if ( WC()->cart->cart_contents_weight < 300 ) {
if ( isset( $rates['fedex:PRIORITY_OVERNIGHT'], $rates['fedex:FEDEX_2_DAY'], $rates['fedex:FEDEX_GROUND'] ) );
} else {
if ( isset( $rates['flat_rate:10'] ) ) unset( $rates['fedex:PRIORITY_OVERNIGHT'], $rates['fedex:FEDEX_2_DAY'],$rates['fedex:FEDEX_GROUND'] );
}
return $rates;
}
Then I found another piece of code that could set a shipping class to a shipping option. The code I found was mean to unset shipping options that are available but I want to isset the freight shipping since we unset it via the cart weight. However, I am running into an issue where the freight shipping class can no longer be set to isset without causing major issues. Below is what I tried to do for the freight shipping.
add_filter( 'woocommerce_package_rates', 'freight_shipping_class_only', 10, 2 );
function freight_shipping_class_only( $rates, $package ) {
$shipping_class_target = 193;
$in_cart = false;
foreach( WC()->cart->cart_contents as $key => $values ) {
if( $values[ 'data' ]->get_shipping_class_id() == $shipping_class_target ) {
$in_cart = true;
break;
}
}
if( $in_cart ) {
unset( $rates['fedex:PRIORITY_OVERNIGHT'], $rates['fedex:FEDEX_2_DAY'],$rates['fedex:FEDEX_GROUND'] ); // shipping method with ID (to find it, see screenshot below)
isset( $rates['flat_rate:10'] );
}
return $rates;
}
Is there a way to set both the cart weight and the freight shipping class in one function? It would be ideal since I want them both to do the exact same thing by unset all FedEx shipping option and isset the freight shipping option.
Let me know if you need me to be clearer or if you have any tips.
Thanks!
As you are using 2 times the same hook, you can simply merge your functions in one.
Now with php [isset()][1] you need to use it in as a condition in an IF statement to check if a variable is set (exist), but it has no effect alone outside your IF statement in your function.
So in your first function the following doesn't has any effect:
if ( isset( $rates['fedex:PRIORITY_OVERNIGHT'], $rates['fedex:FEDEX_2_DAY'], $rates['fedex:FEDEX_GROUND'] ) );
So you can try this compact and efficient code instead:
add_filter( 'woocommerce_package_rates', 'freight_shipping_class_only', 20, 2 );
function freight_shipping_class_only( $rates, $package ) {
// HERE the targeted shipping class ID
$targeted_shipping_class = 193;
// Loop through cart items and checking
$found = false;
foreach( $package['contents'] as $item ) {
if( $item['data']->get_shipping_class_id() == $targeted_shipping_class ){
$found = true;
break;
}
}
// The condition
if ( isset( $rates['flat_rate:10'] ) && ( WC()->cart->get_cart_contents_weight() >= 300 || $found ) ){
unset( $rates['fedex:PRIORITY_OVERNIGHT'], $rates['fedex:FEDEX_2_DAY'], $rates['fedex:FEDEX_GROUND'] );
}
return $rates;
}
Code goes in function.php file of the active child theme (or active theme). It should work.
I found a solution, it isn't exactly what I wanted but it works. What I did was change the cart weight to anything over 300 pounds unset the FedEx shipping options. Then unset FedEx shipping options for the freight shipping class. Which still left the freight shipping option visible on non-freight items, which I hid using CSS.
I am still open for a better way to do this. So if you have suggestions please send them my way.
Below is what I ended up doing.
add_filter( 'woocommerce_package_rates', 'bbloomer_woocommerce_tiered_shipping', 10, 2 );
function bbloomer_woocommerce_tiered_shipping( $rates, $package ) {
if ( WC()->cart->cart_contents_weight < 300 ) {
if ( isset( $rates['fedex:PRIORITY_OVERNIGHT'], $rates['fedex:FEDEX_2_DAY'], $rates['fedex:FEDEX_GROUND'] ) );
} else {
if ( isset( $rates['flat_rate:10'] ) ) unset( $rates['fedex:PRIORITY_OVERNIGHT'], $rates['fedex:FEDEX_2_DAY'],$rates['fedex:FEDEX_GROUND'] );
}
return $rates;
}
/** Freight Shipping Class Only **/
add_filter( 'woocommerce_package_rates', 'freight_shipping_class_only', 10, 2 );
function freight_shipping_class_only( $rates, $package ) {
$shipping_class_target = 193;
$in_cart = false;
foreach( WC()->cart->cart_contents as $key => $values ) {
if( $values[ 'data' ]->get_shipping_class_id() == $shipping_class_target ) {
$in_cart = true;
break;
}
}
if( $in_cart ) {
unset( $rates['fedex:PRIORITY_OVERNIGHT'], $rates['fedex:FEDEX_2_DAY'],$rates['fedex:FEDEX_GROUND'] );
}
return $rates;
}
Related
I need to deactivate a checkout date picker generated by a plugin when a product on cart is virtual.
Here's the hook they gave for that:
apply_filters('woocommerce_delivery_disabled_dates', $disableDates);
Based on that information, this is my code attempt:
add_filter( 'woocommerce_checkout_fields' , 'disable_dates' );
function disable_dates( $fields ) {
$only_virtual = true;
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
// Check if there are non-virtual products
if ( ! $cart_item['data']->is_virtual() ) $only_virtual = false;
}
if( $only_virtual ) {
apply_filters(‘woocommerce_delivery_disabled_dates’, $disableDates);
}
return $fields;
}
However this does not give the desired result, any advice how to hide the checkout date picker when the cart contains a virtual product?
The main issues is that $disabledDates is undefined - I would however change $fields to $disableDates as it makes a bit more sense. See below:
apply_filters('woocommerce_delivery_disabled_dates', $disableDates);
add_filter( 'woocommerce_checkout_fields' , 'disable_dates' );
function disable_dates( $disableDates ) {
$only_virtual = true;
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
// Check if there are non-virtual products
if ( ! $cart_item['data']->is_virtual() ) $only_virtual = false;
}
if( $only_virtual ) {
apply_filters('woocommerce_delivery_disabled_dates', $disableDates);
}
return $disableDates;
}
The $disableDates variable is the input argument for your callback of the hook which you named $fields ( I think )
Ps. this is just a guess based on the code you posted. There is quite a bit that is not clear to me, but $disableDates in your original code clearly should have a value.
No need to use the woocommerce_checkout_fields hook,
the logic can be applied in the appropriate hook.
So you get:
function filter_woocommerce_delivery_disabled_dates( $disableDates ) {
// WC Cart NOT null
if ( ! is_null( WC()->cart ) ) {
// Loop through cart items
foreach ( WC()->cart->get_cart_contents() as $cart_item_key => $cart_item ) {
// Check if there are virtual products
if ( $cart_item['data']->is_virtual() ) {
$disableDates = true;
break;
}
}
}
return $disableDates;
}
add_filter( 'woocommerce_delivery_disabled_dates', 'filter_woocommerce_delivery_disabled_dates', 10, 1 );
We have a large variety of products in WooCommerce, some of which are not our own and supplied. I am trying to set up a functionality where free shipping is disabled if the products in the cart belongs to "Extra" shipping class exclusively.
If there are any other products with it that are not in the shipping class 'Extra', free shipping would still apply. So if 5 products are in cart all with shipping class 'Extra' and no other products, there is a $5 charge. If there is any other product that is not in that shipping class, free shipping applies again.
I've scoured the internet for solutions and this is what I got so far:
function hide_shipping_methods( $available_shipping_methods, $package ) {
$shipping_classes = array( 'Extra', 'some-shipping-class-2' );
$excluded_methods = array( 'free_shipping' );
$found = $others = false;
$shipping_class_exists = false;
foreach( $package['contents'] as $key => $value )
if ( in_array( $value['data']->get_shipping_class(), $shipping_classes ) ) {
$shipping_class_exists = true;
break;
}else {
$others = true; // NOT the shipping class
break;
}
if ( $shipping_class_exists && !others) {
$methods_to_exclude = array();
foreach( $available_shipping_methods as $method => $method_obj )
if ( in_array( $method_obj->method_id, $excluded_methods ) )
$methods_to_exclude[] = $method;
if ( $methods_to_exclude )
foreach ( $methods_to_exclude as $method )
unset( $available_shipping_methods[$method] );
}
return $available_shipping_methods;
}
add_filter( 'woocommerce_package_rates', 'hide_shipping_methods', 10, 2 );
However it doesn't seem to be working. I have products already in the shipping class Extra, however whenever I add them to cart there is still free shipping.
There are some mistakes and your code can be simplified. To disable free shipping for specific shipping classes exclusively, try the following revisited code:
add_filter( 'woocommerce_package_rates', 'hide_free_shipping_conditionally', 10, 2 );
function hide_free_shipping_conditionally( $rates, $package ) {
// Define the targeted shipping classes slugs (not names)
$targeted_classes = array( 'extra' );
$found = $others = false; // Initializing
// Loop through cart items for current shipping package
foreach( $package['contents'] as $item ) {
if ( in_array( $item['data']->get_shipping_class(), $targeted_classes ) ) {
$found = true;
} else {
$others = true;
}
}
// When there are only items from specific shipping classes exclusively
if ( $found && ! $others ) {
// Loop through shipping methods for current shipping package
foreach( $rates as $rate_key => $rate ) {
// Targetting Free shipping methods
if ( 'free_shipping' === $rate->method_id ) {
unset($rates[$rate_key]); // Remove free shipping option(s)
}
}
}
return $rates;
}
Code goes in functions.php file of the active child theme (or active theme). It should work.
Doin't forget to empty your cart to refresh shipping cached data.
I've used a Business Bloomer snippet and tweaked it a bit to unset some shipping methods depending on a shipping class of items in the cart.
It works fine with the 4 shipping methods I have tested with but in order to fully work on my website, I have to list all shipping methods values manually and I have A LOT ( 86 to unset in if and 60 in else.)
Therefore, I would like to edit the snipped so I could unset all shipping methods that contains the same term all at once rather than look for each value individually, but don't really know how to.
I have prints for sale and so I’ve set some shipping methods for orders including prints and some for orders whitout. Therefore all my shipping methods for prints have a value ending with “_print” when the others have a value ending with “_classique”, as you can see in the code bellow.
My goal is to disable all shipping methods ending with “_classique” when a product with the “print” shipping class in the cart and vice versa.
So far my code looks like this :
add_filter( 'woocommerce_package_rates', 'businessbloomer_hide_regular_shipping_method', 10, 2 );
function businessbloomer_hide_regular_shipping_method( $rates, $package ) {
$shipping_class_target = 35; // shipping class ID
$in_cart = false;
foreach( WC()->cart->get_cart_contents() as $key => $values ) {
if( $values[ 'data' ]->get_shipping_class_id() == $shipping_class_target ) {
$in_cart = true;
break;
}
}
if( $in_cart ) { // shipping method with value
unset( $rates['wbs:2:d6f790a0_colissimo_sans_signature_classique'] );
unset( $rates['wbs:2:d748dcd4_lettre_suivie_classique'] );
unset( $rates['wbs:2:f1058bc8_colissimo_avec_signature_classique'] );
}
else{
unset( $rates['wbs:2:1cdf4913_colissimo_sans_signature_print'] );
unset( $rates['wbs:2:fghla482_lettre_suivie_print'] );
unset( $rates['wbs:2:g27a1f56_colissimo_avec_signature_print'] );
}
return $rates;
}
Any help on how to achieve this would be very appreciated.
Thank you to anyone who'll take time to read this !
Updated
Using PHP strpos() will allow you to check if a word is contained in a string. Then you will need to make some changes in your code to make it work:
add_filter( 'woocommerce_package_rates', 'show_hide_shipping_methods', 10, 2 );
function show_hide_shipping_methods( $rates, $package ) {
$shipping_class_id = 35; // Targeted shipping class ID
$found = false;
// Loop through cart items for the current package
foreach ( $package['contents'] as $cart_item ) {
if( $cart_item['data']->get_shipping_class_id() == $shipping_class_id ) {
$found = true;
break;
}
}
// Loop through shipping rates
foreach ( $rates as $rate_key => $rate ) {
if ( $found && strpos($rate_key, '_classique') !== false ) {
unset($rates[$rate_key]);
}
elseif (! $found && strpos($rate_key, '_print') !== false ) {
unset($rates[$rate_key]);
}
}
return $rates;
}
Code goes in functions.php file of the active child theme (or active theme). It should works.
Clearing shipping caches:
You will need to empty your cart, to clear cached shipping data
Or In shipping settings, you can disable / save any shipping method, then enable back / save.
Related:
How can I check if a word is contained in another string using PHP?
Hide shipping methods for specific shipping class in WooCommerce
Filter Shipping method based on shipping class in Woocommerce 3
I'm trying to write a function for WooCommerce that will disable free shipping if a product with a certain shipping class is present in the cart.
Here is what I have which is not working. e-packet is the shipping class that should disable free shipping if it is present in the cart.
function hide_shipping_when_class_is_in_cart( $rates, $package ) {
$shipping_classes = array('e-packet');
$if_exists = false;
foreach( $package['contents'] as $key => $values ) {
if( in_array( $values[ 'data' ]->get_shipping_class(), $shipping_classes ) )
$if_exists = true;
}
if( $if_exists ) unset( $rates['free_shipping:9'] );
return $rates;
}
add_filter( 'woocommerce_package_rates', 'hide_shipping_when_class_is_in_cart', 10, 2 );
Based on similar: Unsetting WooCommerce shipping method based on cart items shipping classes
I have make very small changes. This code is tested and works (see the note at the end):
add_filter( 'woocommerce_package_rates', 'hide_shipping_when_class_is_in_cart', 100, 2 );
function hide_shipping_when_class_is_in_cart( $rates, $package ) {
// Initialisation
$free_shipping_method = 'free_shipping:9';
$shipping_classes = array('e-packet');
$class_exists = false;
foreach( $package['contents'] as $cart_item )
if( in_array( $cart_item['data']->get_shipping_class(), $shipping_classes ) ) {
$class_exists = true;
break; // Stop the loop
}
if( $class_exists )
unset( $rates[$free_shipping_method] );
return $rates;
}
Code goes in function.php file of your active child theme (or active theme).
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.
I try to disable a shipping method if a specific shipping class is in the cart. I'm using the newest woocommerce version.
Below is my Code for my task.
It's placed at the end of my functions.php file of my Theme.
Sadly its not working.
add_filter( 'woocommerce_package_rates', 'businessbloomer_hide_free_shipping_for_shipping_class', 10, 2 );
function businessbloomer_hide_free_shipping_for_shipping_class( $rates, $package ) {
$shipping_class_target = 513; // ID OF MY SHIPPING_CLASS
$in_cart = false;
foreach( WC()->cart->cart_contents as $key => $values ) {
if( $values[ 'data' ]->get_shipping_class_id() == $shipping_class_target ) {
$in_cart = true;
break;
}
}
if( $in_cart ) {
unset( $rates['flat_rate:2'] ); //VALUE:ID OF MY SHIPPING METHOD
}
return $rates;
}
I have tested simplifying a little your code (with the ids of my WC settings) and it works:
add_filter( 'woocommerce_package_rates', 'custom_hide_shipping_methods', 10, 2 );
function custom_hide_shipping_methods( $rates, $package ) {
foreach( WC()->cart->get_cart() as $cart_item ) {
$product = $cart_item[ 'data' ]; // The WC_Product object
$shipping_class_id = $product->get_shipping_class_id();
if( isset($rates['flat_rate:2']) && $shipping_class_id == 513 ) { // <== ID OF MY SHIPPING_CLASS
unset( $rates['flat_rate:2'] ); // Removing specific shipping method
break; // we stop the loop
}
}
return $rates;
}
So your code should work too (if you have set the correct IDs)
BUT you need (after saving your code to the function.php file of your active theme):
To remove all cart items that are remaining in cart when testing.
To refresh the shipping caches:
To do it, you can go in a shipping zone and disable one "flat rate" (for example) and "save". Then re-enable that "flat rate" and "save". You are done.
Now you can test again and it should work