You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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');
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 23:54:11