WooCommerce禁止免费与付费商品一同购买的代码优化咨询
WooCommerce 免费与付费商品互斥购物功能优化
需求明确
要实现:购物车中存在免费(0定价)商品时,自动移除所有付费商品;反之,购物车有付费商品时,自动移除所有免费商品,两者无法同时下单。
原代码的问题
- 价格判断错误:用
WC()->cart->get_product_price()获取的是带货币符号的格式化字符串(比如"$0.00"),直接和数字0比较会失效。 - 移除不彻底:移除付费商品时用了
break,只会删掉第一个付费商品,不是全部。 - 逻辑不完整:只处理了「存在免费商品时移除付费商品」的情况,没处理「存在付费商品时移除免费商品」的反向逻辑。
- 钩子时机不合适:
template_redirect钩子会在页面跳转时触发,不如专门处理购物车计算的woocommerce_before_calculate_totals精准高效。
优化后的代码
add_action( 'woocommerce_before_calculate_totals', 'exclude_free_paid_products_in_cart', 10, 1 ); function exclude_free_paid_products_in_cart( $cart ) { // 避免重复执行(该钩子可能触发多次) if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 ) { return; } $has_free_items = false; $has_paid_items = false; // 遍历购物车,标记是否存在免费/付费商品 foreach ( $cart->get_cart() as $cart_item ) { $product_price = (float) $cart_item['data']->get_price(); if ( $product_price <= 0 ) { $has_free_items = true; } else { $has_paid_items = true; } } // 两者同时存在时执行移除逻辑 if ( $has_free_items && $has_paid_items ) { // 移除所有付费商品(优先保留免费商品) foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) { $product_price = (float) $cart_item['data']->get_price(); if ( $product_price > 0 ) { $cart->remove_cart_item( $cart_item_key ); wc_add_notice( '免费商品与付费商品无法同时购买,已移除购物车中的付费商品', 'notice' ); } } // 若需要优先保留付费商品,可替换为下面的逻辑: // foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) { // $product_price = (float) $cart_item['data']->get_price(); // if ( $product_price <= 0 ) { // $cart->remove_cart_item( $cart_item_key ); // wc_add_notice( '付费商品与免费商品无法同时购买,已移除购物车中的免费商品', 'notice' ); // } // } } }
优化点说明
- 用
get_price()获取原始价格:直接获取商品的数值价格,转成float类型后比较,避免字符串匹配错误。 - 完整的互斥逻辑:覆盖两种共存场景,确保只要两者同时存在就执行对应移除操作。
- 彻底移除对应商品:遍历所有购物车商品,移除符合条件的全部商品,而非单个。
- 合适的钩子:
woocommerce_before_calculate_totals是购物车操作的专用钩子,触发时机精准,还通过did_action避免重复执行。 - 添加用户提示:用
wc_add_notice告知用户商品被移除的原因,提升购物体验。
内容的提问来源于stack exchange,提问作者netnet
相关产品推荐
相关产品推荐

