使用action和filter自定义结算字段

使用action和filter自定义结算字段应该是你熟悉的一项任务。

如果你不熟悉代码和处理潜在的冲突,官方有一个扩展可以帮助你:Checkout Field Editor。安装并启用这个扩展覆盖下面你尝试执行的任何代码;而且当该扩展启用时,你的functions.php文件中不能有自定义结算字段代码。

注意:根据我们的支持政策,我们无法为自定义提供支持。如果您不熟悉代码/模板及如何解决潜在的冲突,请选择Woo专家或开发者以获得帮助。
自定义代码应该被复制到你子主题的functions.php文件中。

结算字段怎样被加载到WooCommerce?

结算页面的账单和配送字段从国家class(class-wc-countries.php)和get_address_fields函数获取。这允许WooCommerce基于用户的位置启用/停用字段。

返回这些字段前,WooCommerce将这些字段放到一个filter中。这允许它们被第三方插件、主题或你自己的自定义代码编辑。

Billing 账单:

$address_fields = apply_filters('woocommerce_billing_fields', $address_fields);

Shipping 配送:

$address_fields = apply_filters('woocommerce_shipping_fields', $address_fields);

结算class将已加载的字段添加到它的checkout_fields数组,也像“order notes 订单备注”一样添加一些其它字段。

$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')
        )
    );
这个数组也可以通过一个filter传递:
$this->checkout_fields = apply_filters('woocommerce_checkout_fields', $this->checkout_fields);

那意味着你对结算字段有完全控制-你仅需要知道怎样访问它们。

覆盖核心字段

hook进入woocommerce_checkout_fieldsfilter,让你可以覆盖任何字段。举个例子,让我们更改order_comments字段的placeholder。现在,它被设置为:

_x('Notes about your order, e.g. special notes for delivery.', 'placeholder', 'woocommerce')

我们可以通过将一个函数添加到我们主题的functions.php文件中以更改这个:

// Hook in
add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' );

// Our hooked in function - $fields is passed via the filter!
function custom_override_checkout_fields( $fields ) {
     $fields['order']['order_comments']['placeholder'] = 'My new placeholder';
     return $fields;
}
你可以覆盖其它部分,比如label:
// Hook in
add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' );

// Our hooked in function - $fields is passed via the filter!
function custom_override_checkout_fields( $fields ) {
     $fields['order']['order_comments']['placeholder'] = 'My new placeholder';
     $fields['order']['order_comments']['label'] = 'My new label';
     return $fields;
}

或删除字段:

// Hook in
add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' );

// Our hooked in function - $fields is passed via the filter!
function custom_override_checkout_fields( $fields ) {
     unset($fields['order']['order_comments']);

     return $fields;
}

这是传递到woocommerce_checkout_fields的数组中的字段的完整列表:

每个字段包含一个属性数组:

在特定情况下,你需要使用woocommerce_default_address_fieldsfilter。这个filter被应用到所有账单和配送默认字段:

例如,将address_1字段从必填变为可选:

// Hook in
add_filter( 'woocommerce_default_address_fields' , 'custom_override_default_address_fields' );

// Our hooked in function - $address_fields is passed via the filter!
function custom_override_default_address_fields( $address_fields ) {
     $address_fields['address_1']['required'] = false;

     return $address_fields;
}

定义select选择框的option选项

如果你正在使用类型“select”添加一个字段,如上所述,你应该定义key/value键值对。例如:

$fields['billing']['your_field']['options'] = array(
  'option_1' => 'Option 1 text',
  'option_2' => 'Option 2 text'
);

优先级

当一些代码-称为一个函数-与页面加载相关运行时,PHP代码的优先级将有帮助。它被设置进每个函数中,当为自定义显示覆盖已有代码时有用。

优先级数字大的代码将在一个数字小的代码的后面运行,意味着优先级为20的代码将在优先级为10的代码的后面运行。

你创建你连接的hook以及你自定义函数的名称后,优先级参数将通过add_action函数被设置。

在下面的例子中,蓝色文字是我们正在修改的hook的名称,绿色文字是我们自定义函数的名称,而红色文字是我们设置的优先级。

例子

在这个例子中,该代码被设置为,将购物车页面的“Return to Shop 返回商店”按钮,以http://example.com/category/specials/重定向到一个列出指定分类产品的分类归档页。

/**
 * Changes the redirect URL for the Return To Shop button in the cart.
 */
function wc_empty_cart_redirect_url() {
  return 'http://example.com/category/specials/';
}
add_filter( 'woocommerce_return_to_shop_redirect', 'wc_empty_cart_redirect_url', 10 );

在那里,我们可以看到优先级被设置为10.这是WooCommerce的函数和script脚本的默认值,所以可能无法有效覆盖那个按钮的功能。

相反,我们可以将优先级更改为任何大于10的数字。虽然11可以,但最佳实践要求我们使用10,20,30等等的增量。

/**
 * Changes the redirect URL for the Return To Shop button in the cart.
 */
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 );

有了优先级,我们可以有两个函数操作同一个hook。通常这会造成各种各样的问题,但由于我们已经建立了一个比另一个更高的优先级,我们的网站将只加载适当的功能,而且我们将通过下面代码按照预期被带到指定页面。

/**
 * Changes the redirect URL for the Return To Shop button in the cart.
 * BECAUSE THIS FUNCTION HAS THE PRIORITY OF 20, IT WILL RUN AFTER THE FUNCTION BELOW (HIGHER NUMBERS RUN LATER)
 */
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 );
/**
 * Changes the redirect URL for the Return To Shop button in the cart.
 * EVEN THOUGH THIS FUNCTION WOULD NORMALLY RUN LATER BECAUSE IT'S CODED AFTERWARDS, THE 10 PRIORITY IS LOWER THAN 20 ABOVE
 */
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

// Hook in
add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' );

// Our hooked in function - $fields is passed via the filter!
function custom_override_checkout_fields( $fields ) {
     $fields['shipping']['shipping_phone'] = array(
        'label'     => __('Phone', 'woocommerce'),
    'placeholder'   => _x('Phone', 'placeholder', 'woocommerce'),
    'required'  => false,
    'class'     => array('form-row-wide'),
    'clear'     => true
     );

     return $fields;
}

/**
 * Display field value on the order edit page
 */
 
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>'.__('Phone From Checkout Form').':</strong> ' . get_post_meta( $order->get_id(), '_shipping_phone', true ) . '</p>';
}

我们怎样处理新字段?不用。因为我们在checkout_fields数组中定义该字段,该字段已被自动处理并保存到订单文章meta(在这个按钮中是:_shipping_phone)。如果你想要添加验证规则,查看你能使用的附加hook的结算class。

添加一个自定义指定字段

添加一个自定义字段是相似的。让我们,通过hook进以下代码,在订单备注后面,添加一个新字段:

/**
 * Add the field to the checkout
 */
add_action( 'woocommerce_after_order_notes', 'my_custom_checkout_field' );

function my_custom_checkout_field( $checkout ) {

    echo '<div id="my_custom_checkout_field"><h2>' . __('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>';

}

这给了我们:

下面,我们需要当结算表单提交时验证该字段。例如该字段是必填还是可选:

/**
 * Process the checkout
 */
add_action('woocommerce_checkout_process', 'my_custom_checkout_field_process');

function my_custom_checkout_field_process() {
    // Check if set, if its not set add an error.
    if ( ! $_POST['my_field_name'] )
        wc_add_notice( __( 'Please enter something into this new shiny field.' ), 'error' );
}

如果该字段是空,将显示一个结算错误:

最后,让我们使用以下代码,将该新字段保存到订单自定义字段中:

/**
 * Update the order meta with field value
 */
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'] ) ) {
        update_post_meta( $order_id, 'My Field', sanitize_text_field( $_POST['my_field_name'] ) );
    }
}

该字段现在被保存到订单中了。

如果你想要在后台菜单编辑页显示该自定义字段的值,你可以添加这个代码:

/**
 * Display field value on the order edit page
 */
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>'.__('My Field').':</strong> ' . get_post_meta( $order->id, '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;
}

将自定义字段添加到电子邮件中

要将一个自定义字段的值添加到WooCommerce电子邮件中-例如一个已完成的订单电子邮件-使用以下片段:

/* To use: 
1. Add this snippet to your theme's functions.php file
2. Change the meta key names in the snippet
3. Create a custom field in the order post - e.g. key = "Tracking Code" value = abcdefg
4. When next updating the status, or during any other event which emails the user, they will see this field in their email
*/
add_filter('woocommerce_email_order_meta_keys', 'my_custom_order_meta_keys');

function my_custom_order_meta_keys( $keys ) {
     $keys[] = 'Tracking Code'; // This will look for a custom field called 'Tracking Code' and add it to emails
     return $keys;
}

翻译不准确?

到QQ群里吐槽:

WooCommerce + WordPress开发