使用操作和过滤器自定义结账字段

如果您不熟悉代码以及解决潜在的冲突，我们有一个扩展程序可以提供帮助：WooCommerce 结账字段编辑器。安装并激活此扩展程序会覆盖您尝试实现的任何代码；当扩展程序激活时，您不能在 
functions.php 文件中包含自定义结账字段的代码。
自定义代码应复制到子主题的 functions.php 文件中。
注意
文档的某些部分仅适用于短代码 Checkout，要向 Checkout 块添加字段，请参阅 额外的结账字段文档。
WooCommerce 如何加载结账字段？
结账过程中的账单和配送字段，来源于 
class-wc-countries.php 类以及 
get_address_fields 函数。 这使得 WooCommerce 可以根据用户的 位置 启用/禁用字段。
在返回这些字段之前，WooCommerce 会将这些字段经过一个 过滤器。 这允许第三方插件、主题以及您的 自定义代码 对其进行编辑。
账单：
$address_fields = apply_filters( 'woocommerce_billing_fields', $address_fields );
配送：
$address_fields = apply_filters( 'woocommerce_shipping_fields', $address_fields );
结账类将加载的字段添加到其 
checkout_fields 数组中，并添加一些其他字段，例如 "订单备注"。
$this->checkout_fields['billing']  = $woocommerce->countries->get_address_fields( $this->get_value( 'billing_country' ), 'billing_' );
$this->checkout_fields['shipping'] = $woocommerce->countries->get_address_fields( $this->get_value( 'shipping_country' ), 'shipping_' );
$this->checkout_fields['account']  = array(
    'account_username'   => array(
        'type'        => 'text',
        'label'       => __( 'Account username', 'woocommerce' ),
        'placeholder' => _x( 'Username', 'placeholder', 'woocommerce' ),
    ),
    'account_password'   => array(
        'type'        => 'password',
        'label'       => __( 'Account password', 'woocommerce' ),
        'placeholder' => _x( 'Password', 'placeholder', 'woocommerce' ),
        'class'       => array( 'form-row-first' )
    ),
    'account_password-2' => array(
        'type'        => 'password',
        'label'       => __( 'Account password', 'woocommerce' ),
        'placeholder' => _x( 'Password', 'placeholder', 'woocommerce' ),
        'class'       => array( 'form-row-last' ),
        'label_class' => array( 'hidden' )
    ),
);
$this->checkout_fields['order']   = array(
    'order_comments' => array(
        'type'        => 'textarea',
        'class'       => array( 'notes' ),
        'label'       => __( 'Order Notes', 'woocommerce' ),
        'placeholder' => _x( 'Notes about your order, e.g. special notes for delivery.', 'placeholder', 'woocommerce' )
    )
);
这个数组也会经过一个过滤器：
$this->checkout_fields = apply_filters( 'woocommerce_checkout_fields', $this->checkout_fields );
这意味着您对结账字段拥有 完全的控制权 - 您只需要知道如何 访问 它们。
覆盖核心字段
通过使用 
woocommerce_checkout_fields 过滤器，您可以覆盖任何字段。 例如，让我们更改 
order_comments 字段的占位符。 目前，它设置为：
_x( 'Notes about your order, e.g. special notes for delivery.', 'placeholder', 'woocommerce' );
我们可以通过将一个函数添加到我们的主题 functions.php 文件中来更改它：
// 注册钩子
add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' );
// 注册的函数 - $fields 通过过滤器传递！
function custom_override_checkout_fields( $fields ) {
    $fields['order']['order_comments']['placeholder'] = 'My new placeholder';
    return $fields;
}
您可以覆盖其他部分，例如标签：
// 注册钩子
add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' );
// 注册的函数 - $fields 通过过滤器传递！
function custom_override_checkout_fields( $fields ) {
    $fields['order']['order_comments']['placeholder'] = 'My new placeholder';
    $fields['order']['order_comments']['label']       = 'My new label';
    return $fields;
}
或者移除字段：
// 注册钩子
add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' );
// 注册的函数 - $fields 通过过滤器传递！
function custom_override_checkout_fields( $fields ) {
    unset( $fields['order']['order_comments'] );
    return $fields;
}
以下是传递给 
woocommerce_checkout_fields 的数组中的所有字段列表：
Billing (账单)
billing_first_name
billing_last_name
billing_company
billing_address_1
billing_address_2
billing_city
billing_postcode
billing_country
billing_state
billing_email
billing_phone
Shipping (配送)
shipping_first_name
shipping_last_name
shipping_company
shipping_address_1
shipping_address_2
shipping_city
shipping_postcode
shipping_country
shipping_state
Account (账户)
account_username
account_password
account_password-2
Order (订单)
order_comments
每个字段都包含一个数组，其中包含以下属性：
type - 字段类型 (text, textarea, password, select)
label - 输入字段的标签
placeholder - 输入的占位符
class - 输入的类
required - true 或 false，表示该字段是否是必填项
clear - true 或 false，应用于字段/标签的清除样式
label_class - 标签元素的类
options - 对于下拉框，选项数组 (键 => 值 对)
在特定情况下，您需要使用 
woocommerce_default_address_fields 过滤器。 此过滤器应用于所有账单和配送默认字段。
自定义 WooCommerce 默认地址字段
本指南将介绍如何自定义 WooCommerce 默认的地址字段。
字段：
country
first_name
last_name
company
address_1
address_2
city
state
postcode
例如，要将 
address_1 字段设置为可选：
// 钩子
add_filter( 'woocommerce_default_address_fields' , 'custom_override_default_address_fields' );
// 我们的钩子函数 - `$address_fields` 通过**过滤器**传递！
function custom_override_default_address_fields( $address_fields ) {
    $address_fields['address_1']['required'] = false;
    return $address_fields;
}
说明：
add_filter() 函数用于添加一个钩子，该钩子允许我们修改 WooCommerce 的默认行为。
woocommerce_default_address_fields 是一个过滤器，它允许我们修改默认的地址字段。
custom_override_default_address_fields 是一个自定义函数，用于修改地址字段。
$address_fields 是一个数组，包含所有地址字段的信息。
required 属性用于指定一个字段是否为必填。
false 值表示该字段为可选。
return $address_fields; 语句将修改后的地址字段数组返回。
通过使用上述代码，您可以轻松地自定义 WooCommerce 的地址字段，以满足您的特定需求。 您可以修改任何字段的属性，例如使其成为必填或可选，或者添加自定义属性。 请记住，在修改任何代码之前，请务必备份您的网站。
定义选择选项
如果您正在添加一个类型为 'select' 的字段，如上所述，您需要定义键/值对。例如：
$fields['billing']['your_field']['options'] = array(
    'option_1' => 'Option 1 text',
    'option_2' => 'Option 2 text'
);
优先级
在 PHP 代码中，优先级用于确定一段代码（称为函数）在网页加载过程中执行的时机。它在每个函数内部设置，并且在覆盖现有代码以进行自定义显示时非常有用。
具有较高数值的优先级代码将在具有较低数值的优先级代码之后运行，这意味着优先级为 20 的代码将在优先级为 10 的代码之后运行。
优先级参数是在 add_action 函数中设置的，在您确定要连接的钩子以及自定义函数的名称之后。
在下面的示例中，蓝色文本是我们要修改的钩子的名称，绿色文本是我们的自定义函数的名称，红色是设置的优先级。
示例
修改“返回商店”按钮的重定向 URL
在这个示例中，代码被设置为将购物车中找到的“返回商店”按钮重定向到 
http://example.url/category/specials/ 所在的分类页面。
/**
 * 更改购物车中“返回商店”按钮的重定向 URL。
 */
function wc_empty_cart_redirect_url() {
    return 'http://example.url/category/specials/';
}
add_filter( 'woocommerce_return_to_shop_redirect', 'wc_empty_cart_redirect_url', 10 );
在那里，我们可以看到优先级被设置为 10。 这是 WooCommerce 函数和脚本的典型默认值，因此可能不足以覆盖该按钮的功能。
相反，我们可以将优先级更改为大于 10 的任何数字。 虽然 11 可以工作，但最佳实践建议我们使用 10 的倍数，例如 20、30 等。
/**
 * 更改购物车中“返回商店”按钮的重定向 URL。
 */
function wc_empty_cart_redirect_url() {
    return 'http://example.com/category/specials/';
}
add_filter( 'woocommerce_return_to_shop_redirect', 'wc_empty_cart_redirect_url', 20 );
通过优先级，我们可以拥有两个函数作用于同一个钩子。 通常，这会导致各种问题，但由于我们已经确定一个函数的优先级高于另一个函数，因此我们的站点只会加载适当的函数，并且我们将按照以下代码的预期被重定向到“特价”页面。
/**
 * 更改购物车中“返回商店”按钮的重定向 URL。
 * 因为这个函数具有 20 的优先级，所以它将在下面的函数之后运行（更高的数字运行在后面）。
 */
function wc_empty_cart_redirect_url() {
    return 'http://example.com/category/specials/';
}
add_filter( 'woocommerce_return_to_shop_redirect', 'wc_empty_cart_redirect_url', 20 );
/**
 * 更改购物车中“返回商店”按钮的重定向 URL。
 * 即使这个函数通常会在后面运行，因为它的代码在后面，但其 10 的优先级低于上面的 20。
 */
function wc_empty_cart_redirect_url() {
    return 'http://example.com/shop/';
}
add_filter( 'woocommerce_return_to_shop_redirect', 'wc_empty_cart_redirect_url', 10 );
添加自定义的配送和账单字段
添加字段的方式与覆盖字段类似。例如，让我们添加一个新的字段到配送字段中 - 
shipping_phone：
// 钩子
add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' );
// 我们的钩子函数 - $fields 通过过滤器传递！
function custom_override_checkout_fields( $fields ) {
     $fields['shipping']['shipping_phone'] = array(
        'label'       => __( '电话', 'woocommerce' ),
        'placeholder' => _x( '电话', '占位符', 'woocommerce' ),
        'required'    => false,
        'class'       => array( 'form-row-wide' ),
        'clear'       => true
     );
     return $fields;
}
/**
 * 在订单编辑页面显示字段值
 */
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'my_custom_checkout_field_display_admin_order_meta', 10, 1 );
function my_custom_checkout_field_display_admin_order_meta($order){
    echo '<p><strong >'. esc_html__( '来自结账表单的电话' ) . ':</strong > ' . esc_html( $order->get_meta( '_shipping_phone', true ) ) . '</p>';
}
它已经生效！
我们用这个新字段做什么？ 没什么。 因为我们在 
checkout_fields 数组中定义了该字段，所以该字段会自动处理并保存到订单的元数据中（在本例中，为 
_shipping_phone）。 如果您想添加验证规则，请参阅结账类，其中有其他您可以使用的钩子。
添加自定义特殊字段
添加自定义字段的方法类似。 让我们在订单备注之后，向结账页面添加一个新的字段，通过以下钩子实现：
/**
 * 将字段添加到结账页面
 */
add_action( 'woocommerce_after_order_notes', 'my_custom_checkout_field' );
function my_custom_checkout_field( $checkout ) {
    echo '<div id="my_custom_checkout_field"><h2>' . esc_html__( 'My Field' ) . '</h2>';
    woocommerce_form_field(
        'my_field_name',
        array(
            'type'        => 'text',
            'class'       => array( 'my-field-class form-row-wide' ),
            'label'       => __( 'Fill in this field' ),
            'placeholder' => __( 'Enter something' ),
        ),
        $checkout->get_value( 'my_field_name' )
    );
    echo '</div>';
}
这样会得到：
接下来，我们需要在提交结账表单时验证该字段。 在这个例子中，我们检查该字段是否只包含字母：
/**
 * 处理结账
 */
add_action( 'woocommerce_checkout_process', 'my_custom_checkout_field_process' );
function my_custom_checkout_field_process() {
    // 检查该字段是否只包含字母。
    if ( ! preg_match( '/^[a-zA-Z]+$/', $_POST['my_field_name'] ) ) {
		wc_add_notice( esc_html__( '请仅在新的漂亮字段中输入字母。' ), 'error' );
	}
}
如果该字段为空，则会显示一个结账错误：
最后，使用以下代码将新的字段保存到订单自定义字段：
/**
 * 使用字段值更新订单元数据
 */
add_action( 'woocommerce_checkout_update_order_meta', 'my_custom_checkout_field_update_order_meta' );
function my_custom_checkout_field_update_order_meta( $order_id ) {
    if ( ! empty( $_POST['my_field_name'] ) ) {
        $order = wc_get_order( $order_id );
        $order->update_meta_data( 'My Field', sanitize_text_field( $_POST['my_field_name'] ) );
        $order->save_meta_data();
    }
}
该字段现在已保存到订单。
如果您希望在管理员订单编辑页面上显示自定义字段的值，您可以添加以下代码：
/**
 * 在订单编辑页面上显示字段值
 */
add_action( 'woocommerce_admin_order_data_after_billing_address', 'my_custom_checkout_field_display_admin_order_meta', 10, 1 );
function my_custom_checkout_field_display_admin_order_meta( $order ){
    echo '<p><strong>' . esc_html__( 'My Field' ) . ':</strong> ' . esc_html( $order->get_meta( 'My Field', true ) ) . '</p>';
}
结果如下：
使电话号码非必填
add_filter( 'woocommerce_billing_fields', 'wc_npr_filter_phone', 10, 1 );
function wc_npr_filter_phone( $address_fields ) {
	$address_fields['billing_phone']['required'] = false;
	return $address_fields;
}