如何阻止WooCommerce在特定条件下保存配送/账单地址?
解决WooCommerce编辑地址表单覆盖默认地址的问题(兼容React结账区块)
核心思路
要实现添加新地址而非覆盖默认,需同时拦截传统表单提交和REST API请求(适配React驱动的新功能),并自定义地址存储逻辑。
1. 拦截传统账户页面的地址保存动作
之前移除动作无效大概率是优先级问题,默认地址保存动作优先级为10,需用更高优先级(比如9)先移除,再替换成自定义保存逻辑:
add_action( 'woocommerce_customer_save_address', 'custom_prevent_default_address_overwrite', 9 ); function custom_prevent_default_address_overwrite( $load_address ) { // 移除默认地址保存动作 remove_action( 'woocommerce_customer_save_address', 'WC_Customer::save_address', 10 ); // 判断是否为添加新地址请求 if ( isset( $_POST['save_address'] ) && isset( $_POST['is_new_address'] ) ) { $current_user = wp_get_current_user(); $address_type = $load_address; // 取值为'billing'或'shipping' // 收集并清洗地址数据 $new_address = array( 'first_name' => sanitize_text_field( $_POST[$address_type.'_first_name'] ), 'last_name' => sanitize_text_field( $_POST[$address_type.'_last_name'] ), 'company' => sanitize_text_field( $_POST[$address_type.'_company'] ), 'address_1' => sanitize_text_field( $_POST[$address_type.'_address_1'] ), 'address_2' => sanitize_text_field( $_POST[$address_type.'_address_2'] ), 'city' => sanitize_text_field( $_POST[$address_type.'_city'] ), 'state' => sanitize_text_field( $_POST[$address_type.'_state'] ), 'postcode' => sanitize_text_field( $_POST[$address_type.'_postcode'] ), 'country' => sanitize_text_field( $_POST[$address_type.'_country'] ), 'phone' => $address_type === 'billing' ? sanitize_text_field( $_POST['billing_phone'] ) : '' ); // 读取现有地址列表并添加新地址 $saved_addresses = get_user_meta( $current_user->ID, 'custom_'.$address_type.'_addresses', true ); $saved_addresses = is_array( $saved_addresses ) ? $saved_addresses : array(); $saved_addresses[] = $new_address; update_user_meta( $current_user->ID, 'custom_'.$address_type.'_addresses', $saved_addresses ); // 跳转提示 wc_add_notice( __( '新地址已添加', 'your-plugin-text-domain' ) ); wp_safe_redirect( wc_get_page_permalink( 'myaccount' ) . '/edit-address/' . $address_type ); exit; } }
2. 适配React结账区块的REST API拦截
React区块通过REST API处理地址操作,需拦截地址创建/更新的钩子:
add_filter( 'woocommerce_rest_insert_customer_address', 'custom_rest_prevent_default_address_overwrite', 10, 3 ); function custom_rest_prevent_default_address_overwrite( $address, $request, $creating ) { // 检查自定义标记,判断是否为添加新地址 if ( isset( $request['is_new_address'] ) && $request['is_new_address'] ) { $user_id = get_current_user_id(); $address_type = $address->get_type(); // 转换地址对象为数组并清理冗余字段 $new_address = $address->get_data(); unset( $new_address['id'], $new_address['customer_id'], $new_address['type'] ); // 更新用户地址列表 $saved_addresses = get_user_meta( $user_id, 'custom_'.$address_type.'_addresses', true ); $saved_addresses = is_array( $saved_addresses ) ? $saved_addresses : array(); $saved_addresses[] = $new_address; update_user_meta( $user_id, 'custom_'.$address_type.'_addresses', $saved_addresses ); // 返回自定义响应,跳过默认地址更新逻辑 return new WP_REST_Response( array( 'message' => '新地址已添加', 'address' => $new_address ), 201 ); } return $address; }
3. 给地址表单添加"添加新地址"标记
传统表单(通过钩子插入隐藏字段)
add_action( 'woocommerce_after_edit_address_form', 'custom_add_new_address_hidden_field' ); function custom_add_new_address_hidden_field( $load_address ) { echo '<input type="hidden" name="is_new_address" value="1">'; }
React结账区块(前端JS拦截请求)
document.addEventListener('DOMContentLoaded', function() { const saveBtn = document.querySelector('.wc-block-components-address-form__save-button'); if (saveBtn) { saveBtn.addEventListener('click', function() { const originalFetch = window.fetch; window.fetch = function(url, options) { if (url.includes('/wp-json/wc/v3/customers/me/addresses')) { if (options.body) { const body = JSON.parse(options.body); body.is_new_address = true; options.body = JSON.stringify(body); } } return originalFetch(url, options); }; }); } });
4. 结账时加载自定义地址供选择
传统结账页面
add_filter( 'woocommerce_checkout_fields', 'custom_add_address_selector_to_checkout' ); function custom_add_address_selector_to_checkout( $fields ) { $current_user = wp_get_current_user(); if ( !$current_user->ID ) return $fields; // 添加配送地址选择器 $shipping_addresses = get_user_meta( $current_user->ID, 'custom_shipping_addresses', true ); if ( is_array( $shipping_addresses ) && !empty( $shipping_addresses ) ) { $addr_options = array(); foreach ( $shipping_addresses as $key => $addr ) { $addr_options[$key] = sprintf('%s %s, %s, %s', $addr['first_name'], $addr['last_name'], $addr['address_1'], $addr['city']); } $fields['shipping']['shipping_address_selector'] = array( 'type' => 'select', 'label' => __( '选择已保存的配送地址', 'your-plugin-text-domain' ), 'options' => $addr_options, 'priority' => 5 ); } // 添加账单地址选择器 $billing_addresses = get_user_meta( $current_user->ID, 'custom_billing_addresses', true ); if ( is_array( $billing_addresses ) && !empty( $billing_addresses ) ) { $addr_options = array(); foreach ( $billing_addresses as $key => $addr ) { $addr_options[$key] = sprintf('%s %s, %s, %s', $addr['first_name'], $addr['last_name'], $addr['address_1'], $addr['city']); } $fields['billing']['billing_address_selector'] = array( 'type' => 'select', 'label' => __( '选择已保存的账单地址', 'your-plugin-text-domain' ), 'options' => $addr_options, 'priority' => 5 ); } return $fields; } // 选择地址后自动填充表单 add_action( 'wp_footer', 'custom_fill_address_on_select' ); function custom_fill_address_on_select() { if ( !is_checkout() ) return; ?> <script> document.addEventListener('change', function(e) { const target = e.target; // 处理配送地址选择 if (target.id === 'shipping_address_selector') { jQuery.post('<?php echo admin_url('admin-ajax.php'); ?>', { action: 'get_custom_shipping_address', index: target.value }, function(data) { if (data.success) { const addr = data.data; jQuery('#shipping_first_name').val(addr.first_name); jQuery('#shipping_last_name').val(addr.last_name); jQuery('#shipping_address_1').val(addr.address_1); jQuery('#shipping_address_2').val(addr.address_2); jQuery('#shipping_city').val(addr.city); jQuery('#shipping_state').val(addr.state); jQuery('#shipping_postcode').val(addr.postcode); jQuery('#shipping_country').val(addr.country); } }); } // 处理账单地址选择 if (target.id === 'billing_address_selector') { jQuery.post('<?php echo admin_url('admin-ajax.php'); ?>', { action: 'get_custom_billing_address', index: target.value }, function(data) { if (data.success) { const addr = data.data; jQuery('#billing_first_name').val(addr.first_name); jQuery('#billing_last_name').val(addr.last_name); jQuery('#billing_address_1').val(addr.address_1); jQuery('#billing_address_2').val(addr.address_2); jQuery('#billing_city').val(addr.city); jQuery('#billing_state').val(addr.state); jQuery('#billing_postcode').val(addr.postcode); jQuery('#billing_country').val(addr.country); jQuery('#billing_phone').val(addr.phone); } }); } }); </script> <?php } // AJAX回调:获取配送地址 add_action( 'wp_ajax_get_custom_shipping_address', 'custom_get_shipping_address' ); add_action( 'wp_ajax_nopriv_get_custom_shipping_address', 'custom_get_shipping_address' ); function custom_get_shipping_address() { $user_id = get_current_user_id(); $index = intval( $_POST['index'] ); $addresses = get_user_meta( $user_id, 'custom_shipping_addresses', true ); isset( $addresses[$index] ) ? wp_send_json_success( $addresses[$index] ) : wp_send_json_error(); } // AJAX回调:获取账单地址 add_action( 'wp_ajax_get_custom_billing_address', 'custom_get_billing_address' ); add_action( 'wp_ajax_nopriv_get_custom_billing_address', 'custom_get_billing_address' ); function custom_get_billing_address() { $user_id = get_current_user_id(); $index = intval( $_POST['index'] ); $addresses = get_user_meta( $user_id, 'custom_billing_addresses', true ); isset( $addresses[$index] ) ? wp_send_json_success( $addresses[$index] ) : wp_send_json_error(); }
React结账区块
需通过WooCommerce区块扩展机制开发自定义组件,加载用户保存的地址列表。可参考官方文档的区块注册方法,通过registerBlockType扩展结账地址模块,实现地址选择与表单填充逻辑。
内容的提问来源于stack exchange,提问作者thefatshizms
相关产品推荐
相关产品推荐

