Произвольные поля для вариативных товаров 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 ),
   ) );
}

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

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

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

  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> размещать код


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

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

к содержанию

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

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

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

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
}

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

Теперь надо заставить работать данный пункт. Без 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;
}

Вопросы задаем в комментариях. Всем пока-пока…

9 комментариев
  • Антон

    17.12.2017

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

    • Артем

      06.01.2018

      Хороший вопрос. Ответа пока не знаю, как придумаю, так в статью добавлю

  • Павел

    16.01.2018

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

    • Артем

      17.01.2018

      Передать можно, но придется с js возиться

  • Илья

    17.02.2018

    Подскажите, хтмл код в описании атрибутов как разрешить?

    • Артем

      18.02.2018

      Никак, это связано с безопасностью и код передавать через поле нельзя

  • Александр

    27.02.2018

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

    • Александр

      27.02.2018

      В итоге для обнуления удалил строчку "&& ! empty( $woocommerce__term_prod_var )"

      • Артем

        01.03.2018

        Можно и так, все от условий зависит

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *
php js HTML CSS Code