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 ...(重复条目)
问题根源
- 钩子触发循环:使用
woocommerce_add_to_cart钩子时,调用WC()->cart->add_to_cart()添加附加商品会再次触发该钩子。由于add_additional_products_to_cart函数会将POST中的additional_products数组写入所有新增购物车项的cart_item_data,附加商品的购物车项也会携带该数组,导致钩子反复执行,无限添加附加商品。 - 会话标记失效:全局会话标记
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');
关键修改点
- 更换触发钩子:将
woocommerce_add_to_cart改为woocommerce_after_add_to_cart_action,确保仅在主商品添加完成后执行一次附加商品添加逻辑。 - 添加附加商品标记:添加附加商品时传入
is_additional_product标记,在add_additional_products_to_cart函数中跳过附加商品的additional_products数据保存,避免循环触发。 - 移除无效会话逻辑:由于新钩子仅触发一次,无需全局会话标记,可保留原会话清理函数或直接删除。
- 补充商品图片展示:在复选框区域添加了商品图片,符合需求中的展示要求。
内容的提问来源于stack exchange,提问作者libertarian
相关产品推荐
相关产品推荐

