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

如何阻止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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 02:15:00