WooCommerce. Скрытие полей заказа в зависимости от выбранного способа доставки

Код полностью обновлен и добавлен пример реального кейса с разбором проблем и решений

Иногда нужно сделать так, чтобы в зависимости от способа доставки выводились разные поля на странице оформления заказа. Можно скрывать эти поля стилями или скриптом, но возникает проблема валидации: заказ невозможно будет отправить, потому что не заполнены обязательные поля. Пришлось придумать собственное решение.

Выглядит оно так

Скрытие полей заказа в зависимости от выбранного способа доставки
Внимание!
Весь код необходимо добавлять в файл functions.php через дочернюю тему или например пустой плагин. Прежде чем, вносить какие либо изменения, сделайте бекап сайта.
к содержанию

Шаг первый. Добавляем форму полей во фрагмент

В WooCommerce есть такой функционал фрагментов, в них можно добавлять нужный html и выводить в нужном месте. Итак, добавляем фрагмент

Код изменен. Предыдущий фрагмент будет работ, но если в магазине используется личный кабинет, то будут проблемы. Так как данная форма подключается в файле checkout/form-billing.php, правильнее использовать такой код

/*
 * Добавляем часть формы к фрагменту
 */
add_filter( 'woocommerce_update_order_review_fragments', 'awoohc_add_update_form_billing', 99 );
function awoohc_add_update_form_billing( $fragments ) {

	$checkout = WC()->checkout();
	ob_start();

	echo '<div class="woocommerce-billing-fields__field-wrapper">';

	$fields = $checkout->get_checkout_fields( 'billing' );
	foreach ( $fields as $key => $field ) {
		if ( isset( $field['country_field'], $fields[ $field['country_field'] ] ) ) {
			$field['country'] = $checkout->get_value( $field['country_field'] );
		}
		woocommerce_form_field( $key, $field, $checkout->get_value( $key ) );
	}

	echo '</div>';

	$art_add_update_form_billing              = ob_get_clean();
	$fragments['.woocommerce-billing-fields'] = $art_add_update_form_billing;

	return $fragments;
}

Что изменилось? Если используется личный кабинет, то при переключении методов доставки происходит дублирование полей формы регистрации. Чтобы такого не происходило, во фрагмент добавляем только нужные поля.

к содержанию

Шаг второй. Убираем лишние поля

Предполагается, что способы доставки настроены. Для примера созданы два способа. Теперь требуется указать нужный идентификатор способа доставки. Самый простой способ — посмотреть в исходном коде

Узнать идентификатор способа доставки

Выделенные строки и есть идентификаторы. Теперь достаточно сделать проверку и если получаем нужный способ доставки, удаляем лишние поля

/*
 * Убираем поля для конкретного способа доставки
 */
add_filter( 'woocommerce_checkout_fields', 'awoohc_override_checkout_fields' );
function awoohc_override_checkout_fields( $fields ) {
   // получаем выбранные метод доставки
   $chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
   // проверяем текущий метод и убираем не ненужные поля
   if ( 'free_shipping:1' === $chosen_methods[0] ) {
      unset( $fields['billing']['billing_company'] );
      unset( $fields['billing']['billing_address_1'] );
      unset( $fields['billing']['billing_address_2'] );
      unset( $fields['billing']['billing_city'] );
      unset( $fields['billing']['billing_postcode'] );
      unset( $fields['billing']['billing_country'] );
      unset( $fields['billing']['billing_state'] );
      unset( $fields['billing']['billing_phone'] );
      unset( $fields['billing']['billing_email'] );
   }
   
   return $fields;
}

Это пример. В каждом случае, допиливайте под свой проект. 

к содержанию

Шаг третий. Обновление страницы

Все работает, кроме обновления страницы. Добавим немного магии ajax, так как вся обработка полей на странице «Оформление заказа» происходит через ajax.

Как это работает?

  • ловим выполнение обновления
  • ловим событие выбора способа доставки
  • запускаем прелоад
  • загружаем фрагмент из первого шага
add_action( 'wp_footer', 'awoohc_add_script_update_shipping_method' );
function awoohc_add_script_update_shipping_method() {
	if ( is_checkout() ) {
		?>
		<!--Скроем поле Страна. Если используется поле Страна, то следует убрать скрытие-->
		<style>
			#billing_country_field {
				display: none !important;
			}
		</style>		<!--Выполняем обновление полей при переключении доставки-->
		<script>
            jQuery(document).ready(function ($) {

                $(document.body).on('updated_checkout updated_shipping_method', function (event, xhr, data) {
                    $('input[name^="shipping_method"]').on('change', function () {
                        $('.woocommerce-billing-fields__field-wrapper').block({
                            message: null,
                            overlayCSS: {
                                background: '#fff',
                                'z-index': 1000000,
                                opacity: 0.3
                            }
                        });
                    });
                    var first_name = $('#billing_first_name').val(),
                        last_name = $('#billing_last_name').val(),
                        phone = $('#billing_phone').val(),
                        email = $('#billing_email').val(),
                        
                    $(".woocommerce-billing-fields__field-wrapper").html(xhr.fragments[".woocommerce-billing-fields"]);
                    $(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_first_name"]').val(first_name);
                    $(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_last_name"]').val(last_name);
                    $(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_phone"]').val(phone);
                    $(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_email"]').val(email);
                    $('.woocommerce-billing-fields__field-wrapper').unblock();
                });
            });
		
		</script>
		<?php
	}
}

Код подключается на хук wp_footer, при желании его можно вынести в отдельный файл. Код проверялся на стандартной теме StoreFront. Все должно работать корректно.

Что изменилось? 

В комментариях указали на косяк — при переключении не сохраняются значения полей: Имя, Фамилия, Почта, Телефон. Полечил. Все оказалось гораздо проще.

И еще сделано обновление полей не после обновления методов доставки, а параллельно с этим обновлением

к содержанию

Весь код полностью

Просто запихать в в файл functions.php

/*
 * Добавляем часть формы к фрагменту
 */
add_filter( 'woocommerce_update_order_review_fragments', 'awoohc_add_update_form_billing', 99 );
function awoohc_add_update_form_billing( $fragments ) {
	$checkout = WC()->checkout();
	ob_start();
	?>
	<div class="woocommerce-billing-fields__field-wrapper">
		<?php
		$fields = $checkout->get_checkout_fields( 'billing' );
		foreach ( $fields as $key => $field ) {
			if ( isset( $field['country_field'], $fields[ $field['country_field'] ] ) ) {
				$field['country'] = $checkout->get_value( $field['country_field'] );
			}
			woocommerce_form_field( $key, $field, $checkout->get_value( $key ) );
		}
		?>
	</div>
	<?php
	$art_add_update_form_billing              = ob_get_clean();
	$fragments['.woocommerce-billing-fields'] = $art_add_update_form_billing;
	
	return $fragments;
}

/*
 * Убираем поля для конкретного способа доставки
 */
add_filter( 'woocommerce_checkout_fields', 'awoohc_override_checkout_fields' );
function awoohc_override_checkout_fields( $fields ) {
   // получаем выбранные метод доставки
   $chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
   // проверяем текущий метод и убираем не ненужные поля
   if ( 'free_shipping:1' === $chosen_methods[0] ) {
      unset( $fields['billing']['billing_company'] );
      unset( $fields['billing']['billing_address_1'] );
      unset( $fields['billing']['billing_address_2'] );
      unset( $fields['billing']['billing_city'] );
      unset( $fields['billing']['billing_postcode'] );
      unset( $fields['billing']['billing_country'] );
      unset( $fields['billing']['billing_state'] );
      unset( $fields['billing']['billing_phone'] );
      unset( $fields['billing']['billing_email'] );
   }
   
   return $fields;
}

/*
* Обновление формы
*/
add_action( 'wp_footer', 'awoohc_add_script_update_shipping_method' );
function awoohc_add_script_update_shipping_method() {
	if ( is_checkout() ) {
		?>
		<!--Скроем поле Страна. Если используется поле Страна, то следует убрать скрытие-->
		<style>
			#billing_country_field {
				display: none !important;
			}
		</style>		<!--Выполняем обновление полей при переключении доставки-->
		<script>
            jQuery(document).ready(function ($) {

                $(document.body).on('updated_checkout updated_shipping_method', function (event, xhr, data) {
                    $('input[name^="shipping_method"]').on('change', function () {
                        $('.woocommerce-billing-fields__field-wrapper').block({
                            message: null,
                            overlayCSS: {
                                background: '#fff',
                                'z-index': 1000000,
                                opacity: 0.3
                            }
                        });
                    });
                    var first_name = $('#billing_first_name').val(),
                        last_name = $('#billing_last_name').val(),
                        phone = $('#billing_phone').val(),
                        email = $('#billing_email').val(),
                        
                    $(".woocommerce-billing-fields__field-wrapper").html(xhr.fragments[".woocommerce-billing-fields"]);
                    $(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_first_name"]').val(first_name);
                    $(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_last_name"]').val(last_name);
                    $(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_phone"]').val(phone);
                    $(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_email"]').val(email);
                    $('.woocommerce-billing-fields__field-wrapper').unblock();
                });
            });
		
		</script>
		<?php
	}
}

Вот и все. А теперь реальный кейс

к содержанию

Реальный кейс

Задача

  • два метода доставки: «Самовывоз», «Новая почта»
  • убрать лишние поля на странице «Оформление заказа»
  • добавить дополнительное поля для метода «Новая почта»
  • значение нового поля должно добавляться в заказ и приходить с письмом

Должно получиться так

В методе «Самовывоз» остаются поля:

  • Имя;
  • Город;
  • Телефон;
  • Email.

В методе «Новая почта»

  • Имя;
  • Город;
  • Телефон;
  • Email
  • Номер отделения
к содержанию

Решение

Первое. Добавляем фрагмент

См. Шаг первый

Второе. Добавляем условие переключения

add_filter( 'woocommerce_checkout_fields', 'awoohc_override_checkout_fields' );
function awoohc_override_checkout_fields( $fields ) {
	unset( $fields['billing']['billing_company'] );
	unset( $fields['billing']['billing_address_1'] );
	unset( $fields['billing']['billing_address_2'] );
	unset( $fields['billing']['billing_postcode'] );
	unset( $fields['billing']['billing_company'] );
	unset( $fields['billing']['billing_state'] );
	unset( $fields['billing']['billing_last_name'] );
	$fields['billing']['billing_city']['label']          = 'Город';
	$fields['billing']['billing_first_name']['class'][0] = 'form-row-wide';
	$fields['billing']['billing_email']['class'][0]      = 'form-row-last';
	$fields['billing']['billing_phone']['class'][0]      = 'form-row-first';
	$chosen_methods                                      = WC()->session->get( 'chosen_shipping_methods' );
	if ( 'flat_rate:2' === $chosen_methods[0] ) {
		$fields['billing']['billing_city']['class'][0] = 'form-row-first';
	} else {
		unset( $fields['billing']['billing_number_post_office'] );
	}
	
	return $fields;
}

Обратите внимание! Не нужные поля убираем сразу, а только потом делаем проверку. И проверка при этом обратная — если не выбран нужный метод доставки, то убираем новое поле.

В некоторые поля сразу вносим изменения:

  • В поле «Населенный пункт» меняем ярлык на «Город»
  • В поле «Имя» изменяем ширину на полную
  • В полях «Емайл» и «Телефон» изменяем ширину на половину, причем поле «Телефон» будет выводиться слева, а «Емайл» — справа

Третье. Добавляем новое поле

Добавляем новое поле с именем number_post_office

add_filter( 'woocommerce_default_address_fields', 'awoohc_override_default_address_fields' );
function awoohc_override_default_address_fields( $address_fields ) {
	$address_fields['number_post_office'] = array(
		'label'       => 'Номер отделения',
		'placeholder' => '',
		'required'    => true,
		'class'       => array( 'form-row-last' ),
		'clear'       => true,
		'priority'    => 75,
	);
	
	return $address_fields;
}

Так как для добавления поля используем хук woocommerce_default_address_fields, то реально имя поля будет billing_number_post_office. Именно под таким именем сохраняем поле в метаданные заказа

add_action( 'woocommerce_checkout_update_order_meta', 'awoohc_checkout_field_update_order_meta' );
function awoohc_checkout_field_update_order_meta( $order_id ) {
	
	if ( ! empty( $_POST['billing_number_post_office'] ) ) {
		update_post_meta( $order_id, 'billing_number_post_office', sanitize_text_field( $_POST['billing_number_post_office'] ) );
	}
}

Теперь надо настроить поля заказа, чтобы данные выводились на странице заказа и связанных с ней. Для начала добавим новое поле в общий массив полей при администрировании заказа

add_filter( 'woocommerce_admin_billing_fields', 'awoohc_add_admin_billing_fields', 10 );
function awoohc_add_admin_billing_fields( $address ) {
	$address['billing_number_post_office'] = array(
		'label' => 'Номер отделения',
		'show'  => true,
	);
	
	return $address;
}

Теперь выведем значение поля в адресе

add_filter( 'woocommerce_get_order_address', 'awoohc_add_billing_fields_get_order' );
function awoohc_add_billing_fields_get_order( $address ) {
	$order_id                              = isset( $_GET ) ? wc_get_order_id_by_order_key( $_GET ) : '';
	$address['billing_number_post_office'] = get_post_meta( $order_id, 'billing_number_post_office', true );
	
	return $address;
}

И сразу отформатируем для полей адреса доставки и адреса оплаты

add_filter( 'woocommerce_order_formatted_shipping_address', 'awoohc_add_billing_fields', 10, 2 );
add_filter( 'woocommerce_order_formatted_billing_address', 'awoohc_add_billing_fields', 10, 2 );
function awoohc_add_billing_fields( $fields, $order ) {
	$fields['billing_number_post_office'] = get_post_meta( $order->get_id(), 'billing_number_post_office', true );
	
	return $fields;
}

Приведем внешний адреса к привычному для России и СНГ

add_filter( 'woocommerce_formatted_address_replacements', 'awoohc_formatted_address_replacements', 10, 2 );
function awoohc_formatted_address_replacements( $replacements, $address ) {
	$replacements['{billing_number_post_office}'] = ( isset( $address['billing_number_post_office'] ) && ! empty( $address['billing_number_post_office'] ) ) ?
		'№ отделения: ' . $address['billing_number_post_office'] : '';
	
	return $replacements;
}
add_filter( 'woocommerce_localisation_address_formats', 'awoohc_address_format' );
function awoohc_address_format( $formats ) {
	$formats['RU'] = "{country}\n{postcode}\n{state}\nг.{city}\n{billing_number_post_office}";
	$formats['UA'] = "{country}\n{postcode}\n{state}\nг.{city}\n{billing_number_post_office}";
	
	return $formats;
}

Четвертое. Добавляем обновление через ajax

Все точно так же, как в третьем шаге, только добавляет сохранение значения нового поля.

add_action( 'wp_footer', 'awoohc_add_script_update_shipping_method' );
function awoohc_add_script_update_shipping_method() {
	if ( is_checkout() ) {
		?>
		<!--Скроем поле Страна. Если успользуется поле Страна, то следцет убрать скрытие-->
		<style>
			#billing_country_field {
				display: none !important;
			}
		</style>		<!--Выполняем обновление полей при переключении доставки-->
		<script>
            jQuery(document).ready(function ($) {

                $(document.body).on('updated_checkout updated_shipping_method', function (event, xhr, data) {
                    $('input[name^="shipping_method"]').on('change', function () {
                        $('.woocommerce-billing-fields__field-wrapper').block({
                            message: null,
                            overlayCSS: {
                                background: '#fff',
                                'z-index': 1000000,
                                opacity: 0.3
                            }
                        });
                    });
                    var first_name = $('#billing_first_name').val(),
                        last_name = $('#billing_last_name').val(),
                        phone = $('#billing_phone').val(),
                        email = $('#billing_email').val(),
                        number_post_office = $('#billing_number_post_office').val();

                    $(".woocommerce-billing-fields__field-wrapper").html(xhr.fragments[".woocommerce-billing-fields"]);
                    $(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_first_name"]').val(first_name);
                    $(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_last_name"]').val(last_name);
                    $(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_phone"]').val(phone);
                    $(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_email"]').val(email);
                    $(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_email"]').val(number_post_office);
                    $('.woocommerce-billing-fields__field-wrapper').unblock();
                });
            });
		
		</script>
		<?php
	}
}

Вот и весь кейс. Ничего сложного

к содержанию

Выводы

В итоге разобрали как можно сделать вывод полей на странице «Оформление заказа». И даже на пример реального кейса. 

Все должно работать без проблем, если что, пишите в комметариях

Всем удачи! И не забудьте поделиться статьей в соцсетях!