Создание пользовательских атрибутов WooCommerce из плагина
Я создал плагин для импорта товаров в WordPress с WooCommerce. Все работает, кроме атрибутов товаров — я не могу найти способ правильно их импортировать.
Проблема в том, что вместе с товарами я добавляю пользовательские таксономии, которые не определены в админке. Удивительно, но я не нашел метода для этого. Я пробовал wp_insert_term()
с register_taxonomy()
, но они не добавляют ничего в таблицу wp_woocommerce_attribute_taxonomies
в базе данных, и я не вижу их в атрибутах товара или на странице атрибутов в админке. Я нашел их только в таблице wp_terms
, но, насколько я понял, это не то место, где должны храниться атрибуты товаров в WooCommerce.
update_post_meta()
тоже не подходит (он может добавлять атрибуты как метаданные, но мне нужны именно атрибуты WooCommerce).
Мне нужно создавать атрибуты, если они не существуют, перед импортом товаров. Есть ли функция для этого, которую я упустил?
Не уверен, нужно ли публиковать мой текущий код, но вот часть, связанная с проблемой:
// Пример данных, полученных из AJAX-запроса
$product_attributes = array(
"attr_id_01" => array(
"name" => "Материал",
"value" => "Металл",
"is_visible" => 1,
"is_taxonomy" => 1
),
"attr_id_02" => array(
"name" => "Тип",
"value" => "С ручкой",
"is_visible" => 1,
"is_taxonomy" => 1
)
);
foreach ($product_attributes_copy as $key => $value) {
// Фильтр sanitize_title предоставляется плагином CyrToLat,
// он преобразует строку в URL-формат,
// используется, так как названия и значения могут содержать кириллицу, заглавные буквы и пробелы
$filtered_name = apply_filters('sanitize_title', $value['name']);
$filtered_value = apply_filters('sanitize_title', $value['value']);
$taxonomy = 'pa_' . $filtered_name;
$parent_term = term_exists($filtered_value, $taxonomy);
$parent_term_id = $parent_term['term_id'];
if (!taxonomy_exists($taxonomy)) {
register_taxonomy($taxonomy, 'product', array('label' => $value['name']));
}
// Следующий код выполняется без ошибок
$insert_result = wp_insert_term(
$filtered_value,
$taxonomy,
array(
'description' => '',
'slug' => $filtered_value,
'parent' => $parent_term_id
)
);
}

По какой-то причине WooCommerce не хочет, чтобы вы делали это вручную. Не совсем понятно почему, так как необходимость делать это вручную создаёт проблемы с масштабируемостью во многих случаях (не говоря уже о том, что если у вас есть атрибуты с большим количеством возможных значений, интерфейс, который они предоставляют, загружается очень медленно). После небольшого исследования, вот приватные функции, которые используют административные страницы, модифицированные для системного вызова.
function process_add_attribute($attribute)
{
global $wpdb;
// check_admin_referer( 'woocommerce-add-new_attribute' );
if (empty($attribute['attribute_type'])) { $attribute['attribute_type'] = 'text';}
if (empty($attribute['attribute_orderby'])) { $attribute['attribute_orderby'] = 'menu_order';}
if (empty($attribute['attribute_public'])) { $attribute['attribute_public'] = 0;}
if ( empty( $attribute['attribute_name'] ) || empty( $attribute['attribute_label'] ) ) {
return new WP_Error( 'error', __( 'Пожалуйста, укажите название атрибута и его ярлык.', 'woocommerce' ) );
} elseif ( ( $valid_attribute_name = valid_attribute_name( $attribute['attribute_name'] ) ) && is_wp_error( $valid_attribute_name ) ) {
return $valid_attribute_name;
} elseif ( taxonomy_exists( wc_attribute_taxonomy_name( $attribute['attribute_name'] ) ) ) {
return new WP_Error( 'error', sprintf( __( 'Ярлык "%s" уже используется. Пожалуйста, измените его.', 'woocommerce' ), sanitize_title( $attribute['attribute_name'] ) ) );
}
$wpdb->insert( $wpdb->prefix . 'woocommerce_attribute_taxonomies', $attribute );
do_action( 'woocommerce_attribute_added', $wpdb->insert_id, $attribute );
flush_rewrite_rules();
delete_transient( 'wc_attribute_taxonomies' );
return true;
}
function valid_attribute_name( $attribute_name ) {
if ( strlen( $attribute_name ) >= 28 ) {
return new WP_Error( 'error', sprintf( __( 'Ярлык "%s" слишком длинный (максимум 28 символов). Пожалуйста, сократите его.', 'woocommerce' ), sanitize_title( $attribute_name ) ) );
} elseif ( wc_check_if_attribute_name_is_reserved( $attribute_name ) ) {
return new WP_Error( 'error', sprintf( __( 'Ярлык "%s" не разрешён, так как является зарезервированным термином. Пожалуйста, измените его.', 'woocommerce' ), sanitize_title( $attribute_name ) ) );
}
return true;
}
Вызывается следующим образом:
$insert = proccess_add_attribute(array('attribute_name' => 'my-new-slug', 'attribute_label' => 'my-new-attribute', 'attribute_type' => 'text', 'attribute_orderby' => 'menu_order', 'attribute_public' => false));
if (is_wp_error($insert)) { do_something_for_error($insert); }
Поля, связанные с атрибутом: name (стандартный ярлык WP), label (читаемая человеком версия названия атрибута), type (выбор между 'select' и 'text', но если вы создаёте системно, скорее всего, вам нужен text — я модифицировал функцию, чтобы по умолчанию использовался именно он), public (помечен как "Включить архивы" в интерфейсе, я сделал так, чтобы функция по умолчанию использовала значение по умолчанию интерфейса) и orderby (варианты: 'menu_order' (обозначается как "Пользовательская сортировка" в интерфейсе), 'name' (очевидно), 'name_num' (числовое имя) и 'id' (ID термина)).

Я решил это по-другому, но не публиковал решение здесь, потому что оно недостаточно универсальное и не такое элегантное, как хотелось бы (но похоже, что с Woocommerce нет красивого решения для этого...). Думаю, ваш код тоже должен работать, поэтому я отмечу его как ответ. И спасибо за усилия с объяснением!
