如何覆盖已被woocommerce_product_get_price过滤的WooCommerce购物车商品价格?
WooCommerce定价冲突解决方案:同时实现用户专属价与免费赠品
问题背景
开发WooCommerce扩展时,遇到两个功能的冲突:
- 全店范围内根据用户和商品信息动态设置专属价格
- 强制指定部分购物车商品为免费(赠品)
两个功能单独运行正常,但同时启用后,免费商品的0价格会被专属定价函数覆盖。
用户专属定价代码(简化版)
add_filter('woocommerce_product_get_price', 'wcds_custom_price', 10, 2); function wcds_custom_price($price, $product) { if (did_action('woocommerce_product_get_price') >= 2) { return; } // 获取用户与商品对应的专属价格 $user_id = get_current_user_id(); $price = wcds_get_price($product, $user_id); // 此功能正常 return $price; }
免费赠品定价代码(简化版)
add_filter('woocommerce_before_calculate_totals', 'wcds_apply_giveaway_pricing', 999, 1); function wcds_apply_giveaway_pricing($cart) { // 不在后台修改价格 if (is_admin()) { return; } if (did_action('woocommerce_before_calculate_totals') >= 2) { return; } // $giveaway_products示例(实际为动态设置) $giveaway_products = array( '107964' => array ( 'qty' => '2', 'giveaway_post_id' => '127753' ) ); // $total_quantities示例(实际从购物车内容动态获取) $total_quantities = array( '107964' => '5' ); $to_add = array(); // 遍历购物车商品并拆分赠品 foreach ($cart->get_cart() as $cart_item_key => $cart_item) { $product_id = $cart_item['product_id']; if (array_key_exists($product_id, $giveaway_products)) { $total_quantity = $total_quantities[$product_id]; $free_quantity = $giveaway_products[$product_id]['qty']; $free_quantity = min($total_quantity, $free_quantity); $paid_quantity = max($total_quantity - $free_quantity, 0); $to_add[$product_id] = [ 'paid' => $paid_quantity, 'free' => $free_quantity, 'giveaway_post_id' => $giveaway_products[$product_id]['giveaway_post_id'], ]; $cart->remove_cart_item($cart_item_key); // 删除赠品商品的原始订单项 } else { unset($cart_item['giveaway']); // 若不在赠品列表则移除赠品标记 } } // 添加调整数量后的赠品商品到购物车 foreach ($to_add as $product_id => $data) { // 添加付费商品到购物车 if ($data['paid'] > 0) { $cart->add_to_cart($product_id, $data['paid']); } // 添加免费赠品商品到购物车 if ($data['free'] > 0) { $giveaway_id = $data['giveaway_post_id']; $cart_item_key = $cart->add_to_cart($product_id, $data['free'], '', '', [ 'giveaway' => true, 'giveaway_id' => $giveaway_id, ]); // 调整赠品商品价格 $cart_item = $cart->get_cart_item($cart_item_key); $cart_item['data']->set_price(0); $cart_item['data']->set_name('FREE: ' . $cart_item['data']->get_name()); // 可选:修改赠品名称 } } }
冲突核心:woocommerce_product_get_price过滤器会在woocommerce_before_calculate_totals之后多次触发,覆盖通过set_price()设置的0价格。
解决方案
方案1:在专属定价函数中排除赠品
修改用户专属定价函数,判断当前商品是否为购物车中的赠品,若是则跳过价格修改逻辑:
add_filter('woocommerce_product_get_price', 'wcds_custom_price', 10, 2); function wcds_custom_price($price, $product) { if (did_action('woocommerce_product_get_price') >= 2) { return $price; } // 检查当前商品是否为购物车中的赠品 global $woocommerce; if (isset($woocommerce->cart)) { foreach ($woocommerce->cart->get_cart() as $cart_item) { if ($cart_item['product_id'] === $product->get_id() && isset($cart_item['giveaway']) && $cart_item['giveaway'] === true) { return $price; // 保留已设置的0价格 } } } // 获取用户与商品对应的专属价格 $user_id = get_current_user_id(); $price = wcds_get_price($product, $user_id); // 此功能正常 return $price; }
方案2:直接锁定购物车项的价格
在设置赠品价格时,直接修改购物车项的小计和总计,绕过后续价格计算:
修改免费赠品代码中的价格设置部分:
// 添加免费赠品商品到购物车 if ($data['free'] > 0) { $giveaway_id = $data['giveaway_post_id']; $cart_item_key = $cart->add_to_cart($product_id, $data['free'], '', '', [ 'giveaway' => true, 'giveaway_id' => $giveaway_id, ]); // 调整赠品商品价格 $cart_item = $cart->get_cart_item($cart_item_key); $cart_item['data']->set_price(0); // 直接设置购物车项的小计和总计 $cart_item['line_subtotal'] = 0; $cart_item['line_total'] = 0; $cart_item['data']->set_name('FREE: ' . $cart_item['data']->get_name()); // 可选:修改赠品名称 // 保存修改后的购物车项 $cart->cart_contents[$cart_item_key] = $cart_item; }
原理说明
- 方案1通过识别购物车项的
giveaway标记,让专属定价逻辑跳过赠品商品,保留已设置的0价格。 - 方案2直接修改购物车项的
line_subtotal和line_total,强制锁定价格为0,避免后续过滤器覆盖。
内容的提问来源于stack exchange,提问作者Madebymartin
相关产品推荐
相关产品推荐

