WordPress中WooCommerce全局变量传递与条件隐藏配送方法问题
解决WooCommerce中全局变量无法跨钩子传递的问题
你遇到的核心问题是全局变量在WooCommerce的钩子执行流程里不可靠——woocommerce_before_cart只在购物车页面首次加载时触发,而结账页、AJAX更新购物车场景下这个钩子不会运行,导致$shipping变量没被正确初始化;另外全局变量在独立请求(比如AJAX)里无法保留值,这也是拿不到正确值的关键原因。
修复方案:移除全局变量,直接在配送方法钩子中实时检测购物车
最稳妥的做法是把商品类别的检测逻辑封装成可复用函数,然后在hide_shipping_method_based_on_shipping_method里直接调用它,这样不管在购物车页、结账页还是AJAX更新时,都能实时获取当前购物车的状态。
修改后的完整代码如下:
// 封装购物车商品类别检测的复用函数 function check_cart_target_categories() { $hasOfficePack = false; $hasSundayCombo = false; $officePack = array('office pack', '77'); // 类别slug或ID $sundayCombo = array('sunday combo', '85'); // 遍历购物车所有商品 foreach ( WC()->cart->get_cart() as $cart_item ) { if ( has_term( $officePack, 'product_cat', $cart_item['product_id'] ) ) { $hasOfficePack = true; } if ( has_term( $sundayCombo, 'product_cat', $cart_item['product_id'] ) ) { $hasSundayCombo = true; } } // 返回对应的标识(如果同时存在两类商品,这里可按需调整优先级) if ( $hasSundayCombo ) { return 1; } elseif ( $hasOfficePack ) { return 0; } // 无匹配类别时返回null return null; } add_filter( 'woocommerce_package_rates', 'hide_shipping_method_based_on_cart', 20, 2 ); function hide_shipping_method_based_on_cart( $rates, $package ) { // 后台非AJAX请求直接返回原配送方法 if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return $rates; // 获取当前购物车的类别检测结果 $shipping_flag = check_cart_target_categories(); // 根据检测结果设置要隐藏的配送方法 $method_key_ids = array(); if ( $shipping_flag == 0 ) { $method_key_ids = array('flat_rate:2', 'free_shipping:8', 'flat_rate:9'); } elseif( $shipping_flag == 1 ) { $method_key_ids = array('flat_rate:2', 'flat_rate:9'); } // 移除指定配送方法(先检查是否存在,避免PHP警告) foreach( $method_key_ids as $method_key_id ){ if ( isset( $rates[$method_key_id] ) ) { unset($rates[$method_key_id]); } } return $rates; }
关键改进点:
- 移除全局变量依赖:不再靠
woocommerce_before_cart钩子设置全局变量,而是在需要使用的地方实时检测购物车,彻底避免钩子执行顺序和跨请求的问题。 - 复用检测逻辑:把类别检测抽成单独函数,后续如果需要在其他场景(比如购物车提示、结账页通知)使用,直接调用即可。
- 调整钩子优先级:把
woocommerce_package_rates的优先级从1改为20,确保在其他可能修改购物车的钩子执行完成后,再处理配送方法隐藏,保证检测结果准确。 - 增加存在性检查:移除配送方法前先确认该方法存在,避免因方法不存在抛出PHP警告。
补充:用WooCommerce会话存储状态(可选)
如果你需要保留woocommerce_before_cart的逻辑用于其他操作,也可以把检测结果存入WooCommerce会话(比全局变量可靠),示例如下:
add_action('woocommerce_before_cart', 'update_shipping_flag_to_session'); function update_shipping_flag_to_session() { $hasOfficePack = false; $hasSundayCombo = false; $officePack = array('office pack', '77'); $sundayCombo = array('sunday combo', '85'); foreach ( WC()->cart->get_cart() as $cart_item ) { if ( has_term( $officePack, 'product_cat', $cart_item['product_id'] ) ) { $hasOfficePack = true; } if ( has_term( $sundayCombo, 'product_cat', $cart_item['product_id'] ) ) { $hasSundayCombo = true; } } // 将结果存入WooCommerce会话 if ( $hasSundayCombo ) { WC()->session->set('shipping_flag', 1); } elseif ( $hasOfficePack ) { WC()->session->set('shipping_flag', 0); } else { WC()->session->set('shipping_flag', null); } } // 在配送方法钩子中获取会话值 add_filter( 'woocommerce_package_rates', 'hide_shipping_method_via_session', 20, 2 ); function hide_shipping_method_via_session( $rates, $package ) { if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return $rates; $shipping_flag = WC()->session->get('shipping_flag'); // 后续逻辑和之前一致... }
不过这种方式需要注意:购物车商品变化时(比如添加/删除商品),要触发会话更新,否则会话值会过时。所以第一种实时检测的方式通常更稳定可靠。
内容的提问来源于stack exchange,提问作者AFS18
相关产品推荐
相关产品推荐

