I just wrote this filter to disable non-free shipping methods when free shipping is available:
add_filter( 'woocommerce_package_rates', 'disable_paid_shipping', 9999, 2 );
function disable_paid_shipping( $rates, $package ) {
$free_rates = array();
foreach ( $rates as $i => $rate ) {
if ( str_contains( $rate->label, "gratuita" ) OR str_contains( $rate->label, "gratuito" ) ) {
$free_rates[] = $rate;
}
if ( str_contains( $rate->id, "local") ) {
$local = $rate;
}
if ( str_contains( $rate->id, "fermopoint") ) {
$fermopoint = $rate;
}
}
if ( !empty( $free_rates ) ) {
if ( isset($fermopoint) ) {
$fermopoint->cost = 0;
$fermopoint->label .= ' gratuito';
array_unshift( $free_rates, $fermopoint );
}
if ( isset($local) ) {
$free_rates[] = $local;
}
$rates = $free_rates;
}
return $rates;
}
The code works as expected, unless for two unexpected events occurring:
To solve the 1st problem at checkout, I can work around by forcing the selection through a hook on woocommerce_before_cart
(although this looks like a forced trick).
For the 2nd problem I have no idea.
Suggestions?
The problem lies in that the WC $rates array is an associative array:
$rates = Array (
[rate_id_0] => [rate_obj_0]
[rate_id_1] => [rate_obj_1]
...
);
While the newly created $free_rates is an indexed array:
$free_rates = Array (
[0] => [rate_obj_0]
[1] => [rate_obj_1]
...
);
As result, WC is unable to match the new $free_rates array with the user's default rate_id, which is supposed to be used as the array key.
Here's the working code:
add_filter( 'woocommerce_package_rates', 'disable_paid_shipping', 9999, 2 );
function disable_paid_shipping( $rates, $package ) {
$free_rates = array();
foreach ( $rates as $i => $rate ) {
if ( str_contains( $rate->label, "gratuita" ) OR str_contains( $rate->label, "gratuito" ) ) {
$free_rates[$rate->id] = $rate;
}
if ( str_contains( $rate->id, "local") ) {
$local = $rate;
}
if ( str_contains( $rate->id, "fermopoint") ) {
$fermopoint = $rate;
}
}
if ( !empty( $free_rates ) ) {
if ( isset($fermopoint) ) {
$fermopoint->cost = 0;
$fermopoint->label .= ' gratuito';
$free_rates = array($fermopoint->id => $fermopoint) + $free_rates; // to set it as 1st
}
if ( isset($local) ) {
$free_rates[$local->id] = $local;
}
$rates = $free_rates;
}
return $rates;
}