英文:
Unable to select shipping method after filtering 'woocommerce_package_rates'
问题
我刚写了这个过滤器来在有免费运输时禁用非免费运输方法:
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;
}
该代码按预期运行,但有两个意外事件:
- 不再默认选择任何运输方法(无论是购物车还是结账页面)
 - 在购物车页面中选择一个运输方式后,立即取消选择
 
为了解决第一个问题,在结账时,可以通过woocommerce_before_cart挂钩强制选择(尽管这看起来像是一种强制性的技巧)。
至于第二个问题,我没有想法。
有什么建议吗?
英文:
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:
- no shipping method is selected by default anymore (both in cart and checkout page)
 - when I choose one in the cart page, it gets unselected right after (only in cart page)
 
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?
答案1
得分: 0
问题出在WC的$rates数组是一个关联数组:
$rates = Array ( 
              [rate_id_0] => [rate_obj_0] 
              [rate_id_1] => [rate_obj_1]
              ... 
         );
而新创建的$free_rates是一个索引数组:
$free_rates = Array ( 
              [0] => [rate_obj_0] 
              [1] => [rate_obj_1] 
              ... 
         );
因此,WC无法将新的$free_rates数组与用户的默认rate_id匹配,该默认rate_id应该用作数组键。
以下是工作代码:
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; // 将其设置为第一个
        }
        if ( isset($local) ) {
            $free_rates[$local->id] = $local;
        }
        $rates = $free_rates;
    } 
    return $rates;
}
请注意,这只是代码的翻译部分。如果您有任何其他问题或需要进一步的帮助,请随时提问。
英文:
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;
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论