Настройка полей заказа WooCommerce с помощью actions и filters

Все манипуляции с кодом из этой статьи следует проводить в файле functions.php

Поля оплаты и доставки для формы заказа подтягиваются из класса объявленного в файле class-wc-countries.php и функции get_address_fields(). Это позволяет включать и выключать поля на основе локации пользователя. Перед тем как вернуть эти поля, woocommerce прогоняет их через фильтры. Это позволяет редактировать их с помощью тем и плагинов.

Оплата:

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

Доставка:

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

Класс checkout добавляет загружаемые поля в массив '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')
        )
    );

Этот массив также проходит через фильтр:

$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

// 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

  • 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 – CSS класс для обертки поля input
  • required – true или false, обязательное или нет поле
  • label_class – class элемента label (название поля)
  • options – для полей select набор опций (key => value pairs)

В некоторых случаях может понадобиться использовать фильтр woocommerce_default_address_fields. Они применяется к стандартным полям:

  • country
  • first_name
  • last_name
  • company
  • address_1
  • address_2
  • city
  • state
  • postcode

Для примера, сделаем поле 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;
}

Приоритет

Приоритет помогает определить очередность запуска кода. Он устанавливается внутри каждой функции и полезен при переопределении существующего кода.

Код с большим номером будет запущен после кода с меньшим номером, это значит код с приоритетом 20 будет запущен после кода с приоритетом 10.

Аргумент, задающий приоритет передается в add_action после задания хука и функции, которую вы вешаете на этот хук.

add_action( 'woocommerce_return_to_shop_redirect', 'wc_empty_cart_redirect_url', 10);

Примеры

Редирект кнопки "Вернуться в магазин" из корзины на страницу с распродажей:

/**
 * Changes the redirect URL for the Return To Shop button in the cart.
 */
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 и так далее.

/**
 * 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 );

С приоритетами мы можем иметь две функции на одном хуке. Обычно это может создать проблемы, но так как мы установили для одной функции более высокий приоритет чем для другой, наш сайт будет выполнять только соответствующую из них, и мы получим страницу с распродажей, как это предусмотрено в коде ниже:

/**
 * 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 '

'.__('Phone From Checkout Form').': ' . get_post_meta( $order->get_id(), '_shipping_phone', true ) . '

'; }
добавление полей в woocommerce

Что мы должны сделать с новым полем? Ничего. Потому что мы определили поле в массиве checkout_fields, поле автоматически обработается и сохранится в post meta. Если нужно добавить проверку, то нужно искать дополнительных хуки в классе checkout.

Добавляем произвольные поля

Подобным образом добавляем произвольные поля. Давайте добавим новое поле после поля "Комментарий к заказу" с помощью хука:

/**
 * 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 '

'.__('My Field').': ' . get_post_meta( $order->id, 'My Field', true ) . '

'; }

Получится следующий результат:

Добавление произвольного поля к email письмам

/* 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;
}
Перейти к верхней панели