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

WooCommerce主商品联动附加商品添加遇无限循环问题求助

问题描述

需要实现类似Product Addons的自定义功能:在WooCommerce商品页设置若干关联简单商品为复选框(展示图片、名称与价格),勾选后随主商品一同加入购物车。现有代码存在无限循环问题,日志显示同一附加商品被反复添加,添加会话标记也无法解决。

原代码

// Function to display checkboxes for additional products
function display_additional_products_checkboxes() {
    $additional_products_ids = array(220, 222, 224, 226); // Replace with your product IDs

    echo '<div class="additional-products">';
    echo '<h3>Additional Products</h3>';

    foreach ($additional_products_ids as $product_id) {
        $additional_product = wc_get_product($product_id);
        if ($additional_product) {
            $product_name = $additional_product->get_name();
            $product_price = $additional_product->get_price_html();
            $product_id_sanitized = sanitize_title($additional_product->get_slug());

            echo '<div class="additional-product">';
            echo '<label for="additional_product_' . $product_id_sanitized . '">';
            echo '<input type="checkbox" name="additional_products[]" id="additional_product_' . $product_id_sanitized . '" value="' . $product_id . '">';
            echo $product_name . ' - ' . $product_price;
            echo '</label>';
            echo '</div>';
        }
    }

    echo '</div>';
}
add_action('woocommerce_before_add_to_cart_button', 'display_additional_products_checkboxes');

// Function to process additional products checkboxes
function add_additional_products_to_cart($cart_item_data, $product_id, $variation_id) {
    if (isset($_POST['additional_products']) && !empty($_POST['additional_products'])) {
        $additional_products = wc_clean(wp_unslash($_POST['additional_products']));
        $cart_item_data['additional_products'] = $additional_products;
    }
    return $cart_item_data;
}
add_filter('woocommerce_add_cart_item_data', 'add_additional_products_to_cart', 10, 3);

// Function to display additional products in cart
function display_additional_products_in_cart($item_data, $cart_item) {
    if (isset($cart_item['additional_products']) && !empty($cart_item['additional_products'])) {
        $additional_products = $cart_item['additional_products'];
        $additional_products_names = array();

        foreach ($additional_products as $product_id) {
            $product = wc_get_product($product_id);
            if ($product) {
                $additional_products_names[] = $product->get_name();
            } else {
                error_log("Failed to get additional product with ID: $product_id");
            }
        }

        $item_data[] = array(
            'key'   => __('Additional Products', 'woocommerce'),
            'value' => implode(', ', $additional_products_names),
        );
    }

    return $item_data;
}
add_filter('woocommerce_get_item_data', 'display_additional_products_in_cart', 10, 2);

// Function to add additional products to order
function add_additional_products_to_order($item, $cart_item_key, $values, $order) {
    if (isset($values['additional_products']) && !empty($values['additional_products'])) {
        $additional_products = $values['additional_products'];

        foreach ($additional_products as $product_id) {
            $product = wc_get_product($product_id);
            if ($product) {
                $item->add_meta_data(__('Additional Product', 'woocommerce'), $product->get_name());
            } else {
                error_log("Failed to get additional product with ID: $product_id");
            }
        }
    }
}
add_action('woocommerce_checkout_create_order_line_item', 'add_additional_products_to_order', 10, 4);

// Function to add additional products as separate line items in the cart
function add_additional_products_as_separate_items($cart_item_key, $product_id, $quantity, $variation_id, $variation, $cart_item_data) {
    if (isset($cart_item_data['additional_products']) && !empty($cart_item_data['additional_products'])) {
        if (!isset(WC()->session->additional_products_added)) {
            WC()->session->set('additional_products_added', array());
        }
        $processed = WC()->session->get('additional_products_added');

        $additional_products = $cart_item_data['additional_products'];

        foreach ($additional_products as $additional_product_id) {
            if (!in_array($additional_product_id, $processed)) {
                $additional_product = wc_get_product($additional_product_id);
                if ($additional_product) {
                    error_log("Adding additional product to cart: " . $additional_product->get_name());
                    WC()->cart->add_to_cart($additional_product_id);
                    $processed[] = $additional_product_id; // Mark this additional product as processed
                } else {
                    error_log("Failed to add additional product with ID: $additional_product_id");
                }
            }
        }

        WC()->session->set('additional_products_added', $processed);
    }
}
add_action('woocommerce_add_to_cart', 'add_additional_products_as_separate_items', 10, 6);

// Clear session data after checkout
function clear_additional_products_session() {
    if (isset(WC()->session->additional_products_added)) {
        WC()->session->__unset('additional_products_added');
    }
}
add_action('woocommerce_checkout_order_processed', 'clear_additional_products_session');

日志信息

[25-Jun-2024 04:21:09 UTC] Adding additional product to cart: Present One
[25-Jun-2024 04:21:09 UTC] Adding additional product to cart: Present One
[25-Jun-2024 04:21:09 UTC] Adding additional product to cart: Present One
...(重复条目)

问题根源
  1. 钩子触发循环:使用woocommerce_add_to_cart钩子时,调用WC()->cart->add_to_cart()添加附加商品会再次触发该钩子。由于add_additional_products_to_cart函数会将POST中的additional_products数组写入所有新增购物车项的cart_item_data,附加商品的购物车项也会携带该数组,导致钩子反复执行,无限添加附加商品。
  2. 会话标记失效:全局会话标记additional_products_added无法区分主商品和附加商品的添加操作,且钩子触发时会话更新不及时,无法阻止循环。

修复方案

改用woocommerce_after_add_to_cart_action钩子(仅在主商品添加完成后触发一次),并在添加附加商品时传入专属标记,避免附加商品触发处理逻辑:

修改后的完整代码

// Function to display checkboxes for additional products
function display_additional_products_checkboxes() {
    $additional_products_ids = array(220, 222, 224, 226); // Replace with your product IDs

    echo '<div class="additional-products">';
    echo '<h3>Additional Products</h3>';

    foreach ($additional_products_ids as $product_id) {
        $additional_product = wc_get_product($product_id);
        if ($additional_product) {
            $product_name = $additional_product->get_name();
            $product_price = $additional_product->get_price_html();
            $product_image = $additional_product->get_image();
            $product_id_sanitized = sanitize_title($additional_product->get_slug());

            echo '<div class="additional-product">';
            echo '<label for="additional_product_' . $product_id_sanitized . '">';
            echo $product_image;
            echo '<input type="checkbox" name="additional_products[]" id="additional_product_' . $product_id_sanitized . '" value="' . $product_id . '">';
            echo $product_name . ' - ' . $product_price;
            echo '</label>';
            echo '</div>';
        }
    }

    echo '</div>';
}
add_action('woocommerce_before_add_to_cart_button', 'display_additional_products_checkboxes');

// Function to process additional products checkboxes
function add_additional_products_to_cart($cart_item_data, $product_id, $variation_id) {
    // 仅在主商品添加时保存附加商品ID,附加商品携带专属标记则跳过
    if (isset($_POST['additional_products']) && !empty($_POST['additional_products']) && !isset($cart_item_data['is_additional_product'])) {
        $additional_products = wc_clean(wp_unslash($_POST['additional_products']));
        $cart_item_data['additional_products'] = $additional_products;
    }
    return $cart_item_data;
}
add_filter('woocommerce_add_cart_item_data', 'add_additional_products_to_cart', 10, 3);

// Function to display additional products in cart
function display_additional_products_in_cart($item_data, $cart_item) {
    if (isset($cart_item['additional_products']) && !empty($cart_item['additional_products'])) {
        $additional_products = $cart_item['additional_products'];
        $additional_products_names = array();

        foreach ($additional_products as $product_id) {
            $product = wc_get_product($product_id);
            if ($product) {
                $additional_products_names[] = $product->get_name();
            } else {
                error_log("Failed to get additional product with ID: $product_id");
            }
        }

        $item_data[] = array(
            'key'   => __('Additional Products', 'woocommerce'),
            'value' => implode(', ', $additional_products_names),
        );
    }

    return $item_data;
}
add_filter('woocommerce_get_item_data', 'display_additional_products_in_cart', 10, 2);

// Function to add additional products to order
function add_additional_products_to_order($item, $cart_item_key, $values, $order) {
    if (isset($values['additional_products']) && !empty($values['additional_products'])) {
        $additional_products = $values['additional_products'];

        foreach ($additional_products as $product_id) {
            $product = wc_get_product($product_id);
            if ($product) {
                $item->add_meta_data(__('Additional Product', 'woocommerce'), $product->get_name());
            } else {
                error_log("Failed to get additional product with ID: $product_id");
            }
        }
    }
}
add_action('woocommerce_checkout_create_order_line_item', 'add_additional_products_to_order', 10, 4);

// Function to add additional products as separate line items in the cart
function add_additional_products_as_separate_items() {
    // 仅处理主商品添加操作,获取POST中的附加商品ID
    if (isset($_POST['additional_products']) && !empty($_POST['additional_products'])) {
        $additional_products = wc_clean(wp_unslash($_POST['additional_products']));
        
        foreach ($additional_products as $additional_product_id) {
            $additional_product = wc_get_product($additional_product_id);
            if ($additional_product) {
                // 添加附加商品时传入专属标记,避免触发附加商品处理逻辑
                WC()->cart->add_to_cart($additional_product_id, 1, 0, array(), array('is_additional_product' => true));
                error_log("Added additional product to cart: " . $additional_product->get_name());
            } else {
                error_log("Failed to get additional product with ID: $additional_product_id");
            }
        }
    }
}
// 使用woocommerce_after_add_to_cart_action钩子,仅在主商品添加完成后触发一次
add_action('woocommerce_after_add_to_cart_action', 'add_additional_products_as_separate_items');

// Clear session data after checkout(原会话逻辑已无需保留,可删除)
function clear_additional_products_session() {
    if (isset(WC()->session->additional_products_added)) {
        WC()->session->__unset('additional_products_added');
    }
}
add_action('woocommerce_checkout_order_processed', 'clear_additional_products_session');

关键修改点

  1. 更换触发钩子:将woocommerce_add_to_cart改为woocommerce_after_add_to_cart_action,确保仅在主商品添加完成后执行一次附加商品添加逻辑。
  2. 添加附加商品标记:添加附加商品时传入is_additional_product标记,在add_additional_products_to_cart函数中跳过附加商品的additional_products数据保存,避免循环触发。
  3. 移除无效会话逻辑:由于新钩子仅触发一次,无需全局会话标记,可保留原会话清理函数或直接删除。
  4. 补充商品图片展示:在复选框区域添加了商品图片,符合需求中的展示要求。

内容的提问来源于stack exchange,提问作者libertarian

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 00:04:51