Creación de taxonomías de atributos personalizados para Woocommerce desde un plugin
He creado un plugin para importar productos a Wordpress con Woocommerce. Funciona bien excepto por los atributos de producto - no encuentro la forma de importarlos correctamente.
El problema es que con los productos añado taxonomías personalizadas que no están definidas en el panel. Sorprendentemente no encuentro un método para esto. Probé wp_insert_term()
con register_taxonomy()
, pero estos no añaden nada a la tabla wp_woocommerce_attribute_taxonomies
en la base de datos y no los encuentro en los atributos del producto ni en la página de atributos del producto en el panel. Solo los encontré en la tabla wp_terms
de la base de datos y por lo que aprendí no es donde deberían estar los atributos de producto en Woocommerce.
update_post_meta()
tampoco parece funcionar (puede añadir atributos como meta, pero los necesito como atributos de producto de Woocommerce).
Necesito crear los atributos si no existen antes de importar los productos. ¿Hay alguna función para hacer esto que haya pasado por alto?
No estoy seguro si debería publicar lo que tengo hasta ahora, pero aquí está el código relacionado con el problema:
// Este es un ejemplo de lo que obtengo de mi entrada AJAX
$product_attributes = array(
"attr_id_01" => array(
"name" => "Material",
"value" => "Metal",
"is_visible" => 1,
"is_taxonomy" => 1
),
"attr_id_02" => array(
"name" => "Tipo",
"value" => "Tiene un mango",
"is_visible" => 1,
"is_taxonomy" => 1
)
);
foreach ($product_attributes_copy as $key => $value) {
// sanitize_title es un filtro proporcionado por el plugin CyrToLat,
// básicamente hace que el string sea amigable para URLs,
// se usa porque nombres y valores pueden contener cirílico, mayúsculas y espacios
$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']) );
}
// No hay errores en lo siguiente
$insert_result = wp_insert_term(
$filtered_value,
$taxonomy,
array(
'description'=> '',
'slug' => $filtered_value,
'parent'=> $parent_term_id
)
);
}

Por alguna razón, WooCommerce parece no querer que hagas esto. No estoy seguro de por qué, ya que requerir que se haga manualmente es un problema para la escalabilidad en varios casos (sin mencionar que si tienes atributos con muchos valores posibles, la interfaz que proporcionan carga muy lentamente). Después de investigar un poco, aquí están las funciones privadas que usan las páginas de administración, modificadas para llamadas sistémicas.
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', __( 'Por favor, proporciona un nombre y slug para el atributo.', '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( __( 'El slug "%s" ya está en uso. Cámbialo, por favor.', '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( __( 'El slug "%s" es demasiado largo (máximo 28 caracteres). Acórtalo, por favor.', 'woocommerce' ), sanitize_title( $attribute_name ) ) );
} elseif ( wc_check_if_attribute_name_is_reserved( $attribute_name ) ) {
return new WP_Error( 'error', sprintf( __( 'El slug "%s" no está permitido porque es un término reservado. Cámbialo, por favor.', 'woocommerce' ), sanitize_title( $attribute_name ) ) );
}
return true;
}
Se llama de la siguiente manera:
$insert = proccess_add_attribute(array('attribute_name' => 'mi-nuevo-slug', 'attribute_label' => 'mi-nuevo-atributo', 'attribute_type' => 'text', 'attribute_orderby' => 'menu_order', 'attribute_public' => false));
if (is_wp_error($insert)) { do_something_for_error($insert); }
Los campos asociados con el atributo son el nombre (slug estándar de WP), etiqueta (versión legible por humanos del nombre del atributo), tipo (puedes elegir entre 'select' y 'text', pero si lo estás creando sistémicamente, probablemente quieras 'text' - modifiqué la función para que lo use por defecto), público (etiquetado como "Habilitar archivos" en la interfaz, hice que la función use el valor por defecto de la interfaz), y orderby (opciones son 'menu_order' (referido como 'Orden personalizado' en la interfaz), 'name' (autoexplicativo), 'name_num' (nombre numérico), y 'id' (ID del término))

Lo resolví de manera diferente, pero no publiqué la solución aquí porque no es lo suficientemente genérica y tampoco tan elegante como me gustaría (pero parece que no hay una solución elegante para esto con Woocommerce...). Creo que tu código debería funcionar también, así que lo marcaré como la respuesta. ¡Y gracias por el esfuerzo con la explicación!
