Произвольные поля для вариативных товаров WooCommerce • 1 • Финты WordPress

Произвольные поля для вариативных товаров WooCommerce

Продолжение статьи про создание дополнительных полей для товаров через API WooCommerce, но теперь для вариативных товаров. И разбирать будем на конкретном примере.

Внимание!
Данная статья предназначена в первую очередь для разработчиков и тех, кто хочет подробнее разобраться. Если вы не любите ковыряться в коде, а поля все же нужны, то проще воспользоваться готовым решением. Например, плагином WooCommerce Custom Product Data Fields или WC Factory Fields
Внимание!
Весь код необходимо добавлять в файл functions.php или например пустой плагин. Прежде чем, вносить какие либо изменения, сделайте бекап сайта.

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

Про создание произвольных полей и их всевозможных вариантах читайте в отдельной статье

к содержанию

Шаг первый. Создание поля

Поля для вариативных товаров создаются точно так же, как и для простых используя соответствующую функцию. В нашем варианте — woocommerce_wp_text_input(). Ну и для вывода в нужном месте будем подвешивать на хук woocommerce_product_after_variable_attributes. Получиться такое решение

add_action( 'woocommerce_product_after_variable_attributes', 'art_term_production_fields', 10, 3 );
function art_term_production_fields( $loop, $variation_data, $variation ) {
   woocommerce_wp_text_input( array(
      'id'                => '_term_prod_var[' . $variation->ID . ']', // id поля
      'label'             => 'Срок изготовления', // Надпись над полем
      'description'       => 'Укажи срок изготовления, просто цифры в днях',// Описание поля
      'desc_tip'          => 'true', // Всплывающая подсказка
      'placeholder'       => 'Срок изготовления, дн', // Надпись внутри поля
      'type'              => 'number', // Тип поля
      'custom_attributes' => array( // Произвольные аттрибуты
         'step' => 'any', // Шаг значений
         'min'  => '0', // Минимальное значение
      ),
      'value'             => get_post_meta( $variation->ID, '_term_prod_var', true ),
   ) );
}

Получаем вот такое поле, в каждой вариации товара

Произвольное поле вариативного товара

Можно вывести поле и в другом месте, где-то выше, например. Для этого используйте соответствующие хуки

Произвольные поля для вариативных товаров WooCommerce • 3 • Финты WordPress
  1. woocommerce_variation_options — после чекбоксов
  2. woocommerce_variation_options_pricing — после цены
  3. woocommerce_variation_options_inventory — после статуса остатков
  4. woocommerce_variation_options_dimensions — после размеров
  5. woocommerce_variation_options_tax — после класса доставки

Поле есть, но оно пока ничего не сохраняет. Давайте исправим это.

к содержанию

Шаг второй. Сохранение значений

Что бы поле сохраняло введенные значения, требуется использовать хук woocommerce_save_product_variation. Примерно так

add_action( 'woocommerce_save_product_variation', 'art_save_variation_settings_fields', 10, 2 );
function art_save_variation_settings_fields( $post_id ) {
   $woocommerce__term_prod_var = $_POST['_term_prod_var'][ $post_id ];
   if (isset($woocommerce__term_prod_var) && ! empty( $woocommerce__term_prod_var ) ) {
      update_post_meta( $post_id, '_term_prod_var', esc_attr( $woocommerce__term_prod_var ) );
   }
}

Алгоритм простой:

  • ловим значение в глобальной переменной $_POST;
  • проверяем нужное значение на существование;
  • если все нормально, тогда записываем/перезаписываем значение в поле.

Вот теперь все работает как надо. Поле есть и значения в нем сохраняются. Теперь надо эти значения вывести на странице товара.

к содержанию

Шаг третий. Вывод значений

В вариативных товарах это не совсем простая задача. Для начала, необходимо через хук woocommerce_available_variation добавить значения нашего поля в общий массив вывода данных вариаций. Вот таким образом

add_filter( 'woocommerce_available_variation', 'load_variation_settings_fields' );
function load_variation_settings_fields( $variations ) {
   $variations_time = get_post_meta( $variations['variation_id'], '_term_prod_var', true );
   if ( isset( $variations_time ) && ! empty( $variations_time ) ) {
      $variations['_term_prod_var'] = '<div class="term-production">';
      $variations['_term_prod_var'] .= '<span>Срок изготовления </span>';
      $variations['_term_prod_var'] .= get_post_meta( $variations['variation_id'], '_term_prod_var', true ) . ' дн.';
      $variations['_term_prod_var'] .= '</div>';
   }
   
   return $variations;
}

Алгоритм тот же, что и выше:

  • получаем значение поля;
  • проверяем значение на существование;
  • если все нормально, тогда добавляем значение в массив.

Теперь надо вывести на странице товара. Нужных хуков не нашел, а потому придется воспользоваться заменой файлов. Для этого надо сделать так:

  • создать в коневой папке темы папку woocommerce;
  • в папку woocommerce скопировать из папки templates самого плагина папку single-product и все что в ней находиться;
  • открыть файл variation.php, который будет находиться по адресу ваша_тема/woocommerce/single-product/add-to-cart;

Осталось добавить в файл значение нашего поля

<div class="woocommerce-variation-custom-text-field ">
   {{{ data.variation._term_prod_var }}}
</div>

Примерно так, можно выше или ниже, главное внутри тега <script> размещать код

Произвольные поля для вариативных товаров WooCommerce • 4 • Финты WordPress


В итоге получаем такой вывод, стилями можно навести красоты

Произвольные поля для вариативных товаров WooCommerce • 5 • Финты WordPress

На этом можно было бы и закончить, но возникает проблема: добавлять значения в поле если больше 2-3-х вариаций вручную еще можно, а что делать если этих вариаций десяток или два?

к содержанию

Шаг четвертый. Массовое изменение поля

Для этого есть в WooCommerce специальное решение по массовому заполнению полей всех вариаций сразу. Например, можно указать базовую цену сразу для всех вариаций

Произвольные поля для вариативных товаров WooCommerce • 6 • Финты WordPress

Такую же фичу можно сделать и для нашего поля. Сначала добавим в выпадающий список нужное значение

add_action( 'woocommerce_variable_product_bulk_edit_actions', 'art_actions_variation_settings_fields', 10, 1 );
function art_actions_variation_settings_fields() {
   ?>
   <option data-global="true" value="variation_prod_time">Срок изготовления</option>
   <?php
}

Получиться вот так

Произвольные поля для вариативных товаров WooCommerce • 7 • Финты WordPress

Теперь надо заставить работать данный пункт. Без ajax не обойтись. Добавляем код, который будет подрубать наш скрипт с аяксом сразу в подвал админки

add_action( 'admin_footer', 'art_script_add_all_variation_select' );
function art_script_add_all_variation_select() {
   ?>
   <script>
        jQuery(function ($) {
            jQuery('.wc-metaboxes-wrapper').on('click', 'a.bulk_edit', function (event) {
                var do_variation_term = jQuery('select.variation_actions').val();
                var data = {},
                    value;
                if ('variation_prod_time' === do_variation_term) {

                    value = window.prompt(woocommerce_admin_meta_boxes_variations.i18n_enter_a_value);

                    if (null !== value) {
                        data.value = value;
                    } else {
                        return;
                    }
                    jQuery.ajax({
                        url: woocommerce_admin_meta_boxes_variations.ajax_url,
                        data: {
                            action: 'woocommerce_bulk_edit_variations',
                            security: woocommerce_admin_meta_boxes_variations.bulk_edit_variations_nonce,
                            product_id: woocommerce_admin_meta_boxes_variations.post_id,
                            product_type: jQuery('#product-type').val(),
                            bulk_action: do_variation_term,
                            data: data
                        },
                        type: 'POST',
                        success: function (data) {
                            jQuery('.variations-pagenav .page-selector').val(1).first().change();
                            //console.log(data.product_id);
                        }
                    });

                    jQuery('#woocommerce-product-data').unblock();
                }

            });
        });
   </script>
   <?php
}

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

add_action( 'woocommerce_bulk_edit_variations_default', 'action_woocommerce_bulk_edit_variations_default', 10, 4 );
function action_woocommerce_bulk_edit_variations_default( $bulk_action, $data, $product_id, $variations ) {
   if ( 'variation_prod_time' === $bulk_action ) {
      foreach ( $variations as $variation ) {
         update_post_meta( $variation, '_term_prod_var', $data['value'] );
      }
   }
   exit;
}

Вот и все. Профит! Весь код рабочий и проверен на реальных проектах.

к содержанию

Полностью весь код

Кроме изменений в файлах

/**
 * Создаем текстовое поле
 *
 * @param $loop
 * @param $variation_data
 * @param $variation
 */
add_action( 'woocommerce_product_after_variable_attributes', 'art_term_production_fields', 10, 3 );
function art_term_production_fields( $loop, $variation_data, $variation ) {
   woocommerce_wp_text_input( array(
      'id'                => '_term_prod_var[' . $variation->ID . ']', // id поля
      'label'             => 'Срок изготовления', // Надпись над полем
      'description'       => 'Укажи срок изготовления, просто цифры в днях',// Описание поля
      'desc_tip'          => 'true', // Всплывающая подсказка
      'placeholder'       => 'Срок изготовления, дн', // Надпись внутри поля
      'type'              => 'number', // Тип поля
      'custom_attributes' => array( // Произвольные аттрибуты
                                    'step' => 'any', // Шаг значений
                                    'min'  => '0', // Минимальное значение
      ),
      'value'             => get_post_meta( $variation->ID, '_term_prod_var', true ),
   ) );
}

/**
 * Сохраняем значение поля
 *
 * @param $post_id
 */
add_action( 'woocommerce_save_product_variation', 'art_save_variation_settings_fields', 10, 2 );
function art_save_variation_settings_fields( $post_id ) {
   $woocommerce__term_prod_var = $_POST['_term_prod_var'][ $post_id ];
   if ( isset( $woocommerce__term_prod_var ) && ! empty( $woocommerce__term_prod_var ) ) {
      update_post_meta( $post_id, '_term_prod_var', esc_attr( $woocommerce__term_prod_var ) );
   }
}

/**
 * Добавляем значение поле в массив данных
 *
 * @param $variations
 *
 * @return mixed
 */
add_filter( 'woocommerce_available_variation', 'art_load_variation_settings_fields' );
function art_load_variation_settings_fields( $variations ) {
   $variations_time = get_post_meta( $variations['variation_id'], '_term_prod_var', true );
   if ( isset( $variations_time ) && ! empty( $variations_time ) ) {
      $variations['_term_prod_var'] = '<div class="term-production">';
      $variations['_term_prod_var'] .= '<span>Срок изготовления </span>';
      $variations['_term_prod_var'] .= get_post_meta( $variations['variation_id'], '_term_prod_var', true ) . ' дн.';
      $variations['_term_prod_var'] .= '</div>';
   }
   
   return $variations;
}

/**
 *  Добавляем пункт выпадающего списка в админке
 */
add_action( 'woocommerce_variable_product_bulk_edit_actions', 'art_actions_variation_settings_fields', 10, 1 );
function art_actions_variation_settings_fields() {
   ?>
   <option data-global="true" value="variation_prod_time">Срок изготовления</option>
   <?php
}

/**
 * Добавляем аякс для массового изменения поля
 */
add_action( 'admin_footer', 'art_script_add_all_variation_select' );
function art_script_add_all_variation_select() {
   ?>
   <script>
        jQuery(function ($) {
            jQuery('.wc-metaboxes-wrapper').on('click', 'a.bulk_edit', function (event) {
                var do_variation_term = jQuery('select.variation_actions').val();
                var data = {},
                    value;
                if ('variation_prod_time' === do_variation_term) {

                    value = window.prompt(woocommerce_admin_meta_boxes_variations.i18n_enter_a_value);

                    if (null !== value) {
                        data.value = value;
                    } else {
                        return;
                    }
                    jQuery.ajax({
                        url: woocommerce_admin_meta_boxes_variations.ajax_url,
                        data: {
                            action: 'woocommerce_bulk_edit_variations',
                            security: woocommerce_admin_meta_boxes_variations.bulk_edit_variations_nonce,
                            product_id: woocommerce_admin_meta_boxes_variations.post_id,
                            product_type: jQuery('#product-type').val(),
                            bulk_action: do_variation_term,
                            data: data
                        },
                        type: 'POST',
                        success: function (data) {
                            jQuery('.variations-pagenav .page-selector').val(1).first().change();
                            //console.log(data.product_id);
                        }
                    });

                    jQuery('#woocommerce-product-data').unblock();
                }

            });
        });
   </script>
   <?php
}

/**
 * Сохранение значения для массового изменения
 *
 * @param $bulk_action
 * @param $data
 * @param $product_id
 * @param $variations
 */
add_action( 'woocommerce_bulk_edit_variations_default', 'action_woocommerce_bulk_edit_variations_default', 10, 4 );
function action_woocommerce_bulk_edit_variations_default( $bulk_action, $data, $product_id, $variations ) {
   if ( 'variation_prod_time' === $bulk_action ) {
      foreach ( $variations as $variation ) {
         update_post_meta( $variation, '_term_prod_var', $data['value'] );
      }
   }
   exit;
}
к содержанию

Дополнение. Добавление полей от плагина ACF Pro в вариативные товары и их вывод

Немного странная задача, но если на сайте используется ACF Pro, то можно его прикрутить и к вариативным товарам.

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

Шаг первый. Создание, вывод и сохранение полей

Для примера создадим несколько полей через штатный интерфейс.

Произвольные поля для вариативных товаров WooCommerce • 8 • Финты WordPress

У меня 3 поля:

  • повторитель
  • текст
  • картинка (для вывода использую только ссылку)

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

/**
 * Добавление нового типа поста в типах ACF Pro
 *
 * @param $choices
 *
 * @return mixed
 *
 * @author        unknown
 * @verphp        7.0
 * @testedwith    WC 6.0
 * @testedwith    ACF Pro 5.12
 */
function art_added_to_acf_variation_post( $choices ) {

	$choices['product_variation'] = 'Product Variation';

	return $choices;
}


add_filter( 'acf/location/rule_values/post_type', 'art_added_to_acf_variation_post' );

Поле добавления сниппета, в типах записи ACF появиться новый тип.

Произвольные поля для вариативных товаров WooCommerce • 9 • Финты WordPress

Выбираем новый тип записи Product Variation и сохраняем нашу группу полей

Теперь выведем поля, на вариациях, что бы их можно было заполнять.

/**
 * Вывод полей на вариациях товара
 *
 * @param $loop_index
 * @param $variation_data
 * @param $variation_post
 *
 * @return void
 *
 * @author        unknown
 * @verphp        7.0
 * @testedwith    WC 6.0
 * @testedwith    ACF Pro 5.12
 */
function art_render_fields_to_variation( $loop_index, $variation_data, $variation_post ) {

	$GLOBALS['wc_loop_variation_id'] = $variation_post->ID;

	foreach ( acf_get_field_groups() as $field_group ) {
		acf_render_fields( $variation_post->ID, acf_get_fields( $field_group ) );
	}

	$GLOBALS['wc_loop_variation_id'] = null;
}


add_action( 'woocommerce_product_after_variable_attributes', 'art_render_fields_to_variation', 10, 3 );

Поля появились, но работаю пока некорректно, точнее ни повторитель, ни добавление картинок не работает. Чтобы бы все заработало, добавим еще один сниппет

/**
 * Подключаем скрипт для работы повторителя и други=х полей завязанных на скрипты
 * 
 * @param $field
 *
 * @return void

 *
 * @author        unknown
 *
 * @verphp        7.0
 * @testedwith    WC 6.0
 * @testedwith    ACF Pro 5.12
 */
function art_added_script_after_render( $field ) {

	?>
	<script>
		( function ( $ ) {
				acf.doAction( 'append', $( '#post' ) );
			})( jQuery );
	</script>
	<?php
}

add_action( 'acf/render_field/type=repeater', 'art_added_script_after_render', 10, 1 );

Теперь всё заработало: и повторитель добавляет, и картинки загружаются. Осталось только нужные значения сохранять в базу. Давайте это и сделаем.

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

/**
 * ПОдменя идентификатора поля
 *
 * @param $field
 *
 * @return mixed
 *
 * @author        unknown
 * @verphp        7.0
 * @testedwith    WC 6.0
 * @testedwith    ACF Pro 5.12
 */
function art_prepare_fields_to_variation( $field ) {

	if ( empty( $GLOBALS['wc_loop_variation_id'] ) ) {
		return $field;
	}

	$field['name'] = preg_replace( '/^acf\[/', 'acf_variation[' . $GLOBALS['wc_loop_variation_id'] . '][', $field['name'] );

	return $field;
}

add_filter( 'acf/prepare_field', 'art_prepare_fields_to_variation', 10, 1 );

Теперь можно сохранять нужные значения

/**
 * Сохранение полей
 *
 * @param $variation_id
 * @param $loop_index
 *
 * @return void
 *
 * @author        unknown
 * @verphp        7.0
 * @testedwith    WC 6.0
 * @testedwith    ACF Pro 5.12
 */
function art_save_fields_variations( $variation_id, $loop_index ) {

	if ( ! isset( $_POST['acf_variation'][ $variation_id ] ) ) {
		return;
	}

	if ( ! empty( $_POST['acf_variation'][ $variation_id ] ) && is_array( $fields = $_POST['acf_variation'][ $variation_id ] ) ) {
		foreach ( $fields as $key => $val ) {
			update_field( $key, $val, $variation_id );
		}
	}

}

add_action( 'woocommerce_save_product_variation', 'art_save_fields_variations', 1000, 2 );
к содержанию

Шаг два. Вывод значений полей.

Для вывода потребуется замена файла. Выше уже описано как это сделать, но еще раз продублирую:

  • создать в коневой папке темы папку woocommerce, если её ещё нет;
  • в папку woocommerce скопировать из папки templates самого плагина папку single-product и все что в ней находиться;
  • открыть файл variation.php, который будет находиться по адресу ваша_тема/woocommerce/single-product/add-to-cart;

Пока выводить нечего. потому, давайте в общий массив атрибутов добавим значения наших полей.

/**
 * Добавление значений полей к атрибутам вариаций
 *
 * @param $variations
 *
 * @return mixed
 *
 * @author        unknown
 * @verphp        7.0
 * @testedwith    WC 6.0
 * @testedwith    ACF Pro 5.12
 */
function art_added_fields_to_variation_attributes( $variations ) {

	$variations['repeater']    = get_field( '11', $variations['variation_id'] );
	$variations['text_addon']  = get_field( '333', $variations['variation_id'] );
	$variations['image_addon'] = get_field( '444', $variations['variation_id'] );

	return $variations;
}

add_filter( 'woocommerce_available_variation', 'art_added_fields_to_variation_attributes', 10, 1 );

Если посмотреть инспектор кода, то увидим, что общем объекте атрибутов, появились и наши значения

Произвольные поля для вариативных товаров WooCommerce • 10 • Финты WordPress

Причем, как положено, для каждой вариации, свои значения.

Теперь изменяем файл variation.php

Вывод текстового поля

<div class="woocommerce-variation-text_addon">{{{ data.variation.text_addon }}}</div>

Вывод изображения

Если выводиться массив изображения

<div class="woocommerce-variation-image_addon"><img src='{{{ data.variation.image_addon.url }}}'></div>

В данном случае, из объекта массива изображения берем только url основной картинки, но можно и дргие значения брать

Если изображение ссылкой выводиться

<div class="woocommerce-variation-image_addon"><img src='{{{ data.variation.image_addon }}}'></div>

Вывод повторителя

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

<div class="woocommerce-variation-repeater">
	<ul>
		<# _.each( data.variation.repeater, function( listItem ){ #>
		<li>
			{{{ data.variation.text_two }}}
			<img src='{{{ listItem.image }}}'>
		</li>
		<# }); #>
	</ul>
</div>
к содержанию

Заключение

В целом, основные сложности в вариативных товарах заключатся только в выводе значений. А так как, в вариациях используется шаблонизатор Underscore.js, то глянув на документацию можно вывести любые значения.

Как-то так так. Пишите в коментариях, что думаете.

4.2 13 голоса
Рейтинг статьи

Об авторе
Артем Абрамович

Автор и ведущий проекта «Финты WordPress». WordPress & WooCommerce разработчик. Четыре года создаю плагины и темы. В свободное время пишу статьи, видеообзоры, гайды.

Подписаться
Уведомить о
guest
40 комментарев
Новые
Старые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
40
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x