Поздравляю Вас, коллега, Вы изменили направление этой древней темы в нужное русло, т.к. правильно поставленный вопрос уже содержит в себе половину ответа.
Этой теме уже 4 года, прочитал её всю внимательно, как и ещё 100500 хаков по данному вопросу. И вот к чему пришёл. Проблема терзает людей до сих пор и все в едином порыве смотрят в сторону "магии jQuery". По всему выходит, что разработчики WooCommerce что-то недоработали или скоро доработают. Ведь блок с заказом и доставкой обновляется аяксом, а поля - нет. Значит, рано или поздно должны доработать. Ничего подобного, не должны и не доработают. Поскольку логика формы заказа цельная и здравая: сначала пользователь указывает кому и куда, а потом разбирается с доставкой и оплатой. При всём уважении к
yaspis и действительно работающему методу, это костыль. А костыль - это не дело.
Посудите сами. Если у вас по умолчанию Самовывоз, пользователь пишет только ФИО и контакты. Далее он смотрит на варианты доставки, а там можно ещё и курьером. Он думает: "Хм.. а пусть будет лучше курьером". Он жмёт на "Курьерская доставка" и... тадам! Где-то там наверху появляются новые поля с адресом! Теперь пользователю надо к ним вернуться и заполнить. Если он их сразу заметит, конечно. Это неудобно, это нелогично. Пользователь не должен метаться туда-обратно, заполнение полей должно происходить строго в порядке их следования. И решение здесь может быть только одно: выбрав метод доставки, требующий дополнительных данных, пользователь должен увидеть их здесь же, сразу под названием выбранной доставки.
На самом деле, не нужно писать JavaScript, не нужно отключать обязательность полей, не нужно даже лезть в шаблоны. В WC уже всё есть, чтобы достаточно просто реализовать описанный товарищем
studnet функционал.
Действительно:
1) Вывод дополнительной информации к методу доставки - событие woocommerce_after_shipping_rate
2) Отключение ненужных полей - фильтр woocommerce_checkout_fields
3) Получить текущий выбранный метод доставки - WC()->session->get( 'chosen_shipping_methods' );
4) Вывод полей начинается с woocommerce_before_checkout_form и заканчивается woocommerce_after_checkout_form
5) Отображение итогов корзины, соответственно - woocommerce_before_cart_totals и woocommerce_after_cart_totals
6) Поле отображается на странице функцией woocommerce_form_field
Это всё, что нам нужно, остальное - дело техники. Приведённый ниже код можно скопировать в functions.php вашей темы и он сразу работает. Должен, во всяком случае (WooCommerce 3.4.4). Для тестирования у вас должно быть два метода доставки "Единая ставка" (flat_rate) и "Самовывоз" (local_pickup). Для первого метода будут показаны поля адреса, а для второго - индекс. Это для теста, настройка вся вынесена в функцию "get_shipping_method_fields()". В ней вы можете указывать как метод доставки в формате "flat_rate", так и выделить конкретный метод: "flat_rate:5". Работает и так и эдак.
Код:
/**
* Настройка привязки полей к методам доставки.
*
* Функция возвращает массив полей заказа, которые актуальны
* только для указанных методов доставки и неприменимы для других методов.
*
* Указанные в массиве поля не будут отображаться в стандартных блоках оплаты и доставки.
* Вместо этого они будут выведены после наименования метода доставки
* и только когда он выбран.
*
* Если к определённому виду доставки привязано обязательное поле, но
* выбрана другая доставка, проблем с валидацией не будет, поскольку
* в этом случае поле исключается из обрабатываемых полей стандартным способом.
*
* Массив двумерный: поля сгруппированы по блокам, в соответствии с документацией
* https://docs.woocommerce.com/document/tutorial-customising-checkout-fields-using-actions-and-filters/#section-2
*
* Например, как сделать, чтобы поля адреса были применимы
* только к методу доставки "Единая ставка":
*
return array(
'billing' => array(
'billing_address_1' => array( 'flat_rate' ),
'billing_address_2' => array( 'flat_rate' )
)
);
*/
function get_shipping_method_fields() {
return array(
'billing' => array(
'billing_address_1' => array( 'flat_rate' ),
'billing_address_2' => array( 'flat_rate' ),
'billing_postcode' => array( 'local_pickup' ),
)
);
}
/**
* Проверяет наличие метода доставки в формате типа 'flat_rate:3'
* в массиве методов доставки (method_id - в формате типа 'flat_rate' )
*
* @param type $chosen_shipping
* @param type $methods
* @return type
*/
function in_methods_array($chosen_shipping_id, $method_ids)
{
$found = FALSE;
$i = 0;
$count = count($method_ids);
while ( !$found && ( $i < $count ) ){
$method_id = $method_ids[$i];
$found = ( strpos($chosen_shipping_id, $method_id) !== FALSE );
$i++;
}
return $found;
}
/**
* Скрыть в корзине и блоках оплаты и доставки поля,
* которые имеют привязку к методам доставки.
* Установленная переменная используется в фильтре woocommerce_checkout_fields
*/
add_action('woocommerce_before_cart_totals', 'smf_before_checkout_form');
add_action('woocommerce_before_checkout_form', 'smf_before_checkout_form');
function smf_before_checkout_form(){
set_query_var( 'smf_hide_fields', TRUE );
}
add_action('woocommerce_after_checkout_form', 'smf_after_checkout_form');
add_action('woocommerce_after_cart_totals', 'smf_after_checkout_form');
function smf_after_checkout_form(){
set_query_var( 'smf_hide_fields', FALSE );
}
/**
* Отключить неприменимые к текущему методу доставки поля.
* Или отключить всё поля, привязанные к методам доставки,
* если идёт вывод checkout_form
*/
add_filter('woocommerce_checkout_fields', 'smf_remove_checkout_fields');
function smf_remove_checkout_fields($fields) {
$smf_fields = get_shipping_method_fields();
if ( !empty( $smf_fields ) ){
$hideall = get_query_var('smf_hide_fields') === TRUE;
$chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
$chosen_shipping_id = $chosen_methods[0];
foreach ( $fields as $fieldset => $fieldlist ) {
if ( key_exists( $fieldset, $smf_fields ) ) {
foreach ( $fieldlist as $fieldkey => $field ) {
if ( key_exists($fieldkey, $smf_fields[$fieldset] ) ){
$method_ids = $smf_fields[$fieldset][$fieldkey];
if ( $hideall || !in_methods_array($chosen_shipping_id, $method_ids) ){
unset( $fields[$fieldset][$fieldkey] );
}
}
}
}
}
}
return $fields;
}
/**
* Вывести поля для метода доставки, если он выбран в данный момент
*/
add_action('woocommerce_after_shipping_rate', 'smf_after_shipping_rate');
function smf_after_shipping_rate( $method ) {
$chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
$chosen_shipping_id = $chosen_methods[0];
$smf_fields = get_shipping_method_fields();
if ( !empty( $smf_fields ) ){
// это выбранный метод доставки?
if ( strpos($chosen_shipping_id, $method->id) !== FALSE ){
$checkout = WC()->checkout;
foreach ($smf_fields as $fieldset => $fieldlist) {
$allfields = $checkout->get_checkout_fields( $fieldset );
$fieldkeys = array_keys($fieldlist);
foreach ( $allfields as $fieldkey => $field ) {
$method_ids = $smf_fields[$fieldset][$fieldkey];
if (
in_array($fieldkey, $fieldkeys) &&
in_methods_array($chosen_shipping_id, $method_ids)
)
{
woocommerce_form_field( $fieldkey, $field, $checkout->get_value( $fieldkey ) );
}
}
}
}
}
}