WooCommerce自定义字段与价格计算异常问题求助
WooCommerce自定义商品价格问题:加购时同时计入初始价与自定义价
问题描述
开发WooCommerce插件时,针对戒指类商品实现自定义价格功能,遇到两个核心问题:
- 商品页面自定义后的价格无法正常更新显示
- 点击提交自定义选项并加入购物车时,系统会同时计算商品初始价格与自定义后的价格,导致总价错误
尝试通过移除购物车内同款商品的代码解决,但无效。
完整实现代码
add_action('woocommerce_before_add_to_cart_button', 'add_to_cart_product_pricing'); function add_to_cart_product_pricing() { global $woocommerce, $product, $wpdb; if (is_a($woocommerce, 'WooCommerce') && is_product()) { // Targeting "ring" or "rings" product category if (has_term(array('ring', 'rings'), 'product_cat')) { // Load the latest gold rate $gold_rate_table = $wpdb->prefix . 'gold_rate'; $gold_rate = $wpdb->get_var("SELECT final_price FROM $gold_rate_table ORDER BY id DESC LIMIT 1"); //Get Metal Purity $metal_purity = $product->get_attribute('metal-purity'); $metal_purity_table = $wpdb->prefix . 'diamond_purity'; // Prepare the SQL query with proper escaping $query = $wpdb->prepare("SELECT percentage FROM $metal_purity_table WHERE purity = %s", $metal_purity); // Run the prepared query $metalpurity = $wpdb->get_var($query); $goldvalue = ($gold_rate * $metalpurity) / 100; // Diamond Shape $dquality = $product->get_attribute('diamond-quality'); $sieves = $product->get_attribute('sieves'); $dweight = $product->get_attribute('diamond-weight-ct'); $dpieces = $product->get_attribute('diamond-pieces'); $round_diamond_table = $wpdb->prefix . 'round_diamond'; // Prepare the SQL query with proper escaping $query = $wpdb->prepare("SELECT $sieves FROM $round_diamond_table WHERE diamond_quality = %s", $dquality); // Run the prepared query $dshape = $wpdb->get_var($query); $dtotal = ($dshape * ($dweight / 5)); $dmtotal = $dtotal * $dpieces; // Get net weight product attribute $net_weight = $product->get_attribute('net-weight-g'); // Get product regular price $regular_price = $product->get_regular_price(); // Calculate product updated price $updated_price = ($goldvalue * $net_weight) + $regular_price + $dmtotal; // Get the displayed price $args = array('price' => floatval($updated_price)); if ('incl' === get_option('woocommerce_tax_display_shop')) { $displayed_price = wc_get_price_including_tax($product, $args); } else { $displayed_price = wc_get_price_excluding_tax($product, $args); } // Display product updated price printf('<p class="productprice">%s</p>', wc_price($displayed_price)); // Display a hidden input field with the "updated_price" as value printf('<input type="hidden" name="updated_price" value="%s" />', $updated_price); // Get gross weight product attribute $gross_weight = $product->get_attribute('gross-weight'); // Display the Gross Weight printf('<p class="grossweight">' . __('Weight: %s g') . '</p>', $gross_weight); // Store the updated price in a global variable global $updated_price; } } } add_action('woocommerce_single_product_summary', 'customization_of_product', 32); function customization_of_product() { global $product, $wpdb; if (has_term(array('ring', 'rings'), 'product_cat')) { echo '<div class="customization-form"> <form id="customization-form" method="POST"> <label for="ring-size">Ring Size:</label>'; // Ring Size $ring_size_table = $wpdb->prefix . 'ring_size'; $results = $wpdb->get_results("SELECT * FROM $ring_size_table"); echo '<select name="ring-size" id="ring-size"> <option value="">Ring Sizes</option>'; foreach ($results as $result) { echo '<option value="' . $result->ring_size . '">' . $result->ring_size . '</option>'; } echo '</select><br> <label for="metal-purity">Metal Purity:</label>'; // Metal Purity $table_name = $wpdb->prefix . 'diamond_purity'; $results = $wpdb->get_results("SELECT * FROM $table_name WHERE purity='14kt' OR purity='18kt'"); echo '<select name="metal-purity" id="metal-purity"> <option value="">Metal Purity</option>'; foreach ($results as $result) { echo '<option value="' . $result->purity . '">' . $result->purity . '</option>'; } echo '</select>'; echo '<input type="submit" name="submit" value="Submit"> </form> </div>'; if (isset($_POST['submit'])) { // Get the submitted values $ring_size = $_POST['ring-size']; $metal_purity = $_POST['metal-purity']; // Calculate the updated price $updated_price = calculate_updated_price($product, $ring_size, $metal_purity); // Remove any existing items of the same product from the cart foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item) { if ($cart_item['product_id'] === $product->get_id()) { WC()->cart->remove_cart_item($cart_item_key); } } // Set the updated price in the cart item data $cart_item_data = array( 'updated_price' => $updated_price, 'unique_key' => md5(microtime() . rand()) ); // Add the cart item WC()->cart->add_to_cart($product->get_id(), 1, 0, array(), $cart_item_data); // Get the updated cart value $updated_cart_value = WC()->cart->get_cart_total(); // Display the updated cart value echo 'Updated Cart Value: ' . $updated_cart_value; } } } // Function to calculate the updated price function calculate_updated_price($product, $ring_size, $metal_purity) { global $wpdb; // Load the latest gold rate $gold_rate_table = $wpdb->prefix . 'gold_rate'; $gold_rate = $wpdb->get_var("SELECT final_price FROM $gold_rate_table ORDER BY id DESC LIMIT 1"); // Get Metal Purity percentage $metal_purity_table = $wpdb->prefix . 'diamond_purity'; $query = $wpdb->prepare("SELECT percentage FROM $metal_purity_table WHERE purity = %s", $metal_purity); $metal_purity_percentage = $wpdb->get_var($query); // Calculate gold value $gold_value = ($gold_rate * $metal_purity_percentage) / 100; // Get net weight product attribute $net_weight = $product->get_attribute('net-weight-g'); // Get size gram variation $ring_size_table = $wpdb->prefix . 'ring_size'; $query = $wpdb->prepare("SELECT variation_grams FROM $ring_size_table WHERE ring_size = %s", $ring_size); $ring_grams_percentage = $wpdb->get_var($query); // Get product regular price $regular_price = $product->get_regular_price(); // Diamond Shape $dquality = $product->get_attribute('diamond-quality'); $sieves = $product->get_attribute('sieves'); $dweight = $product->get_attribute('diamond-weight-ct'); $dpieces = $product->get_attribute('diamond-pieces'); $round_diamond_table = $wpdb->prefix . 'round_diamond'; // Prepare the SQL query with proper escaping $query = $wpdb->prepare("SELECT $sieves FROM $round_diamond_table WHERE diamond_quality = %s", $dquality); // Run the prepared query $dshape = $wpdb->get_var($query); $dtotal = ($dshape * ($dweight / 5)); $dmtotal = $dtotal * $dpieces; $default_ring_size = $product->get_attribute('default-ring-size'); if ($default_ring_size < $ring_size) { $netweight = $net_weight + $ring_grams_percentage; // Calculate product updated price $updated_price = ($gold_value * $netweight) + $regular_price + $dmtotal; } elseif ($default_ring_size = $ring_size) { $updated_price = ($gold_value * $net_weight) + $regular_price + $dmtotal; } else { $netweight2 = $net_weight - $ring_grams_percentage; $updated_price = ($gold_value * $netweight2) + $regular_price + $dmtotal; } // Get the displayed price $args = array('price' => floatval($updated_price)); if ('incl' === get_option('woocommerce_tax_display_shop')) { $displayed_price = wc_get_price_including_tax($product, $args); } else { $displayed_price = wc_get_price_excluding_tax($product, $args); } // Display product updated price printf('<p class="productprice">%s</p>', wc_price($displayed_price)); // Display a hidden input field with the "updated_price" as value printf('<input type="hidden" name="updated_price" value="%s" />', $updated_price); return $updated_price; } add_filter('woocommerce_add_cart_item_data', 'save_custom_cart_item_data', 10, 2); function save_custom_cart_item_data($cart_item_data, $product_id) { if (isset($_POST['updated_price']) && !empty($_POST['updated_price'])) { // Set the custom data in the cart item $cart_item_data['updated_price'] = (float) wc_clean($_POST['updated_price']); // Make each item as a unique separated cart item $cart_item_data['unique_key'] = md5(microtime() . rand()); } return $cart_item_data; } add_action('woocommerce_cart_item_price', 'filter_cart_displayed_price', 10, 2); function filter_cart_displayed_price($price, $cart_item) { if (isset($cart_item['updated_price'])) { $args = array('price' => floatval($cart_item['updated_price'])); if ('incl' === get_option('woocommerce_tax_display_cart')) { $product_price = wc_get_price_including_tax($cart_item['data'], $args); } else { $product_price = wc_get_price_excluding_tax($cart_item['data'], $args); } return wc_price($product_price); } return $price; } add_action('woocommerce_before_calculate_totals', 'set_new_cart_item_updated_price'); function set_new_cart_item_updated_price($cart) { if ((is_admin() && !defined('DOING_AJAX'))) return; if (did_action('woocommerce_before_calculate_totals') >= 2) return; // Loop through cart items and set the updated price foreach ($cart->get_cart() as $cart_item) { // Set the new price if (isset($cart_item['updated_price'])) { $cart_item['data']->set_price($cart_item['updated_price']); } } }
尝试过的解决方案
尝试在自定义加购逻辑前移除同款商品,但问题依旧:
// Calculate the updated price $updated_price = calculate_updated_price($product, $ring_size, $metal_purity); // Remove any existing items of the same product from the cart foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item) { if ($cart_item['product_id'] === $product->get_id()) { WC()->cart->remove_cart_item($cart_item_key); } }
问题分析与修复方案
1. 价格计算逻辑的致命错误
在calculate_updated_price函数中,存在赋值运算符代替比较运算符的错误:
// 错误代码:赋值操作而非比较 elseif ($default_ring_size = $ring_size) {
应修改为严格比较:
// 修正为比较运算符 elseif ($default_ring_size === $ring_size) {
这个错误会导致无论选择什么尺寸,都会进入默认尺寸的价格计算分支,引发价格显示异常。
2. 重复添加购物车的冲突
自定义表单提交后,默认的WooCommerce加购按钮可能被触发,导致同时添加默认价格商品和自定义价格商品。解决方法:
- 禁用默认加购按钮,仅通过自定义表单处理加购
- 在
woocommerce_add_to_cart_validation钩子中阻止默认加购逻辑,当存在自定义参数时跳过默认行为
3. 购物车价格设置的优化
原set_new_cart_item_updated_price函数中,直接修改商品对象价格可能引发缓存问题,建议强制更新购物车会话:
add_action('woocommerce_before_calculate_totals', 'set_new_cart_item_updated_price'); function set_new_cart_item_updated_price($cart) { if ((is_admin() && !defined('DOING_AJAX'))) return; if (did_action('woocommerce_before_calculate_totals') >= 2) return; foreach ($cart->get_cart() as $cart_item_key => $cart_item) { if (isset($cart_item['updated_price'])) { $new_price = (float)$cart_item['updated_price']; $cart_item['data']->set_price($new_price); // 强制更新购物车会话 $cart->set_session(); } } }
4. 移除购物车项的逻辑优化
移除同款商品时,需覆盖变体商品,并在添加新项前清空购物车缓存:
if (isset($_POST['submit'])) { $ring_size = $_POST['ring-size']; $metal_purity = $_POST['metal-purity']; $updated_price = calculate_updated_price($product, $ring_size, $metal_purity); // 移除同款商品(包括变体) $cart = WC()->cart; foreach ($cart->get_cart() as $cart_item_key => $cart_item) { if ($cart_item['product_id'] === $product->get_id() || $cart_item['variation_id'] === $product->get_id()) { $cart->remove_cart_item($cart_item_key); } } // 清空购物车会话缓存 $cart->set_session(); $cart_item_data = array( 'updated_price' => $updated_price, 'unique_key' => md5(microtime() . rand()) ); $cart->add_to_cart($product->get_id(), 1, 0, array(), $cart_item_data); // 重定向避免表单重复提交 wp_redirect(get_permalink($product->get_id())); exit; }
5. 页面价格更新的修复
原代码直接输出自定义价格,但未替换默认价格显示,导致页面同时显示两个价格。需移除默认价格钩子,替换为自定义价格:
// 移除默认价格显示 remove_action('woocommerce_single_product_summary', 'woocommerce_template_single_price', 10); // 添加自定义价格显示 add_action('woocommerce_single_product_summary', 'custom_single_product_price', 10); function custom_single_product_price() { global $product; if (has_term(array('ring', 'rings'), 'product_cat')) { // 调用价格计算逻辑并输出 $gold_rate_table = $wpdb->prefix . 'gold_rate'; $gold_rate = $wpdb->get_var("SELECT final_price FROM $gold_rate_table ORDER BY id DESC LIMIT 1"); $metal_purity = $product->get_attribute('metal-purity'); $metal_purity_table = $wpdb->prefix . 'diamond_purity'; $query = $wpdb->prepare("SELECT percentage FROM $metal_purity_table WHERE purity = %s", $metal_purity); $metalpurity = $wpdb->get_var($query); $goldvalue = ($gold_rate * $metalpurity) / 100; $dquality = $product->get_attribute('diamond-quality'); $sieves = $product->get_attribute('sieves'); $dweight = $product->get_attribute('diamond-weight-ct');
相关产品推荐
相关产品推荐

