Reputation: 601
I am building a scripts that gets products, and I am stuck at the part of programmatically adding attributes. So basically I want to check if a attribute exists, if not add it. Then check if it's value exists, if not them add it's value, and attach everything to my product.
Here is the format I get the products as:
[attributes] => Array
(
[0] => stdClass Object
(
[id] => 332
[name] => Gender
[value] => Woman
)
[1] => stdClass Object
(
[id] => 333
[name] => Wheel Size
[value] => 28 Inch
)
[2] => stdClass Object
(
[id] => 334
[name] => Frame height
[value] => 56 cm
)
Now I have tried 2 versions, I'm gonna paste both code samples.
Version 1:
foreach($product->attributes as $attribute) {
$slug = 'pa_' . strtolower($attribute->name);
$attribute_name = $attribute->name;
$attribute_value = $attribute->value;
$permalinks = get_option( 'woocommerce_permalinks' );
$taxonomy_data = array(
'hierarchical' => true,
'update_count_callback' => '_update_post_term_count',
'labels' => array(
'name' => $attribute_name
),
'show_ui' => false,
'query_var' => true,
'sort' => false,
'public' => true,
'show_in_nav_menus' => false,
'capabilities' => array(
'manage_terms' => 'manage_product_terms',
'edit_terms' => 'edit_product_terms',
'delete_terms' => 'delete_product_terms',
'assign_terms' => 'assign_product_terms',
)
);
register_taxonomy( $slug, array('product'), $taxonomy_data );
// @end_Debug
}
This version only added the attributes somehow attached to products, but they weren't visible under the Attributes Tab in Wordpress Admin. I would later want filters by these attributes, so I need the connection with Woocommerce attributes.
Version 2:
foreach($product->attributes as $attribute) {
$slug = 'pa_' . strtolower($attribute->name);
$attribute_name = $attribute->name;
$attribute_value = $attribute->value;
$table = $wpdb->prefix . 'woocommerce_attribute_taxonomies';
$attr = $wpdb->get_results( "SELECT * FROM {$table} WHERE attribute_name LIKE '%{$attribute_name}%'", OBJECT );
if(count($attr) > 0) {
//
}
else {
/** create taxonomy */
$data = array(
'attribute_label' => $attribute_name,
'attribute_name' => $slug,
'attribute_type' => 'select',
'attribute_orderby' => 'menu_order',
'attribute_public' => 0, // Enable archives ==> true (or 1)
);
$results = $wpdb->insert( "{$wpdb->prefix}woocommerce_attribute_taxonomies", $data );
if ( is_wp_error( $results ) ) {
return new WP_Error( 'cannot_create_attribute', $results->get_error_message(), array( 'status' => 400 ) );
}
$id = $wpdb->insert_id;
do_action('woocommerce_attribute_added', $id, $data);
wp_schedule_single_event( time(), 'woocommerce_flush_rewrite_rules' );
/* end_Create */
}
// Clean attribute name to get the taxonomy
$taxonomy = 'pa_' . wc_sanitize_taxonomy_name( $attribute_name );
$option_term_ids = array(); // Initializing
// Loop through defined attribute data options (terms values)
if( term_exists( $attribute_value, $taxonomy ) ){
// Save the possible option value for the attribute which will be used for variation later
wp_set_object_terms( $product_id, $attribute_value, $taxonomy, true );
// Get the term ID
$option_term_ids[] = get_term_by( 'name', $attribute_value, $taxonomy )->term_id;
}
// Loop through defined attribute data
$attributes[$taxonomy] = array(
'name' => $taxonomy,
'value' => $option_term_ids, // Need to be term IDs
'position' => '1',
'is_visible' => '1',
'is_variation' => '1',
'is_taxonomy' => '1'
);
// Save the meta entry for product attributes
update_post_meta( $product_id, '_product_attributes', $attributes );
}
This one seems only to add them under Product Attributes Tabs, also does NOT add the Terms Values.
Upvotes: 6
Views: 16347
Reputation: 19
Here is modified version of the code if someone is looking for the solution to create attributes and there corresponding values on the product create or update.
// Create Product attributes if not exist
function create_product_attribute( $label_name ){
global $wpdb;
$slug = sanitize_title( $label_name );
if ( strlen( $slug ) >= 28 ) {
return new WP_Error( 'invalid_product_attribute_slug_too_long', sprintf( __( 'Name "%s" is too long (28 characters max). Shorten it, please.', 'woocommerce' ), $slug ), array( 'status' => 400 ) );
} elseif ( wc_check_if_attribute_name_is_reserved( $slug ) ) {
return new WP_Error( 'invalid_product_attribute_slug_reserved_name', sprintf( __( 'Name "%s" is not allowed because it is a reserved term. Change it, please.', 'woocommerce' ), $slug ), array( 'status' => 400 ) );
} elseif ( taxonomy_exists( wc_attribute_taxonomy_name( $label_name ) ) ) {
return new WP_Error( 'invalid_product_attribute_slug_already_exists', sprintf( __( 'Name "%s" is already in use. Change it, please.', 'woocommerce' ), $label_name ), array( 'status' => 400 ) );
}
$data = array(
'attribute_label' => $label_name,
'attribute_name' => $slug,
'attribute_type' => 'select',
'attribute_orderby' => 'menu_order',
'attribute_public' => 0, // Enable archives ==> true (or 1)
);
$results = $wpdb->insert( "{$wpdb->prefix}woocommerce_attribute_taxonomies", $data );
if ( is_wp_error( $results ) ) {
return new WP_Error( 'cannot_create_attribute', $results->get_error_message(), array( 'status' => 400 ) );
}
$id = $wpdb->insert_id;
do_action('woocommerce_attribute_added', $id, $data);
wp_schedule_single_event( time(), 'woocommerce_flush_rewrite_rules' );
delete_transient('wc_attribute_taxonomies');
}
//take attributes array with values and attached with the products
function pswc_attach_terms_value_with_product($product_id, $attributes){
$_the_att_data = array();
foreach($attributes as $attribute_k => $attribute_v){
if($attribute_v !==''){
// *** Sanitize the text lower and no spaces
$_att_name = 'pa_'.wc_sanitize_taxonomy_name($attribute_k);
//check and Create attribute if not exist by providing attribute name
create_product_attribute( wc_sanitize_taxonomy_name($attribute_k));
$term_taxonomy_ids = wp_set_object_terms( $product_id, $attribute_v, $_att_name, true );
//build array of attribute with name and values
$_the_att_data[$_att_name] = array(
'name'=>$_att_name,
'value'=>$attribute_v,
'is_visible' => '1',
'is_taxonomy' => '1'
);
}
}
//Update Product attributes array with the product
update_post_meta( $product_id,'_product_attributes',$_the_att_data);
}
//create product
// Get the product ID from the Created Woo Product
//set product attribute Names with Terms
$attributes = array('color'=>'red','size'=>'small','delivery'=>'express','design'=>'yes',);
// Provide Product_id and attributes array with name as key and option value as value to save with product
pswc_attach_terms_value_with_product($product_id, $attributes);
** Provide Product_id and attributes array with name as key and option value as value to save with product. This function will trigger above both function's first check if the attribute exist or not. If not than will register and create one and after that Second function will get values from the attributes array and save/attach with product**
//set product attribute Names with Term Values
$attributes = array('color'=>'red','size'=>'small','delivery'=>'express','design'=>'yes');
//Function to Pass the Product ID and Attribute key value array
pswc_attach_terms_value_with_product($product_id, $attributes);
***You can read the full article on apifixer Blog *** Read the Full Artcicle
Upvotes: 1
Reputation: 6553
Okay so this is quite a complicated task, but this is the code that I got working via looking at other's source code and modifying as necessary.
Firstly you will need these helper functions.
To create a global attribute, you need to create a Taxonomy for each one (Gender, Wheel Size, etc), using this function:
function create_global_attribute($name, $slug)
{
$taxonomy_name = wc_attribute_taxonomy_name( $slug );
if (taxonomy_exists($taxonomy_name))
{
return wc_attribute_taxonomy_id_by_name($slug);
}
//logg("Creating a new Taxonomy! `".$taxonomy_name."` with name/label `".$name."` and slug `".$slug.'`');
$attribute_id = wc_create_attribute( array(
'name' => $name,
'slug' => $slug,
'type' => 'select',
'order_by' => 'menu_order',
'has_archives' => false,
) );
//Register it as a wordpress taxonomy for just this session. Later on this will be loaded from the woocommerce taxonomy table.
register_taxonomy(
$taxonomy_name,
apply_filters( 'woocommerce_taxonomy_objects_' . $taxonomy_name, array( 'product' ) ),
apply_filters( 'woocommerce_taxonomy_args_' . $taxonomy_name, array(
'labels' => array(
'name' => $name,
),
'hierarchical' => true,
'show_ui' => false,
'query_var' => true,
'rewrite' => false,
) )
);
//Clear caches
delete_transient( 'wc_attribute_taxonomies' );
return $attribute_id;
}
Now to apply these to your products, I assume you are using Variable Products which each have Variations? The parent Variable Product needs to have their attribute
property set to a specially formatted array of the attributes. This function creates that array, creating the global taxonomies for each attribute if they are not found.
//$rawDataAttributes must be in the form of array("Color"=>array("blue", "red"), "Size"=>array(12,13,14),... etc.)
function generate_attributes_list_for_product($rawDataAttributes)
{
$attributes = array();
$pos = 0;
foreach ($rawDataAttributes as $name => $values)
{
if (empty($name) || empty($values)) continue;
if (!is_array($values)) $values = array($values);
$attribute = new WC_Product_Attribute();
$attribute->set_id( 0 );
$attribute->set_position($pos);
$attribute->set_visible( true );
$attribute->set_variation( true );
$pos++;
//Look for existing attribute:
$existingTaxes = wc_get_attribute_taxonomies();
//attribute_labels is in the format: array("slug" => "label / name")
$attribute_labels = wp_list_pluck( $existingTaxes, 'attribute_label', 'attribute_name' );
$slug = array_search( $name, $attribute_labels, true );
if (!$slug)
{
//Not found, so create it:
$slug = wc_sanitize_taxonomy_name($name);
$attribute_id = create_global_attribute($name, $slug);
}
else
{
//Otherwise find it's ID
//Taxonomies are in the format: array("slug" => 12, "slug" => 14)
$taxonomies = wp_list_pluck($existingTaxes, 'attribute_id', 'attribute_name');
if (!isset($taxonomies[$slug]))
{
//logg("Could not get wc attribute ID for attribute ".$name. " (slug: ".$slug.") which should have existed!");
continue;
}
$attribute_id = (int)$taxonomies[$slug];
}
$taxonomy_name = wc_attribute_taxonomy_name($slug);
$attribute->set_id( $attribute_id );
$attribute->set_name( $taxonomy_name );
$attribute->set_options($values);
$attributes[] = $attribute;
}
return $attributes;
}
We are almost ready to apply the attributes to products, but the main Variable Product and it's Variations need to have the taxonomy terms assigned to them like any Wordpress taxonomy. So if you have a taxonomy called "Wheel Size" which has the terms "28 Inch" and "30 Inch", then the parent product needs to have all "28 Inch" and "30 Inch" terms assigned to it, and the Variations need to have their individual terms assigned to them, so "28 Inch" for one and "30 Inch" for another. To help with this, we use this function to get and/or create these terms if they don't exist:
function get_attribute_term($value, $taxonomy)
{
//Look if there is already a term for this attribute?
$term = get_term_by('name', $value, $taxonomy);
if (!$term)
{
//No, create new term.
$term = wp_insert_term($value, $taxonomy);
if (is_wp_error($term))
{
//logg("Unable to create new attribute term for ".$value." in tax ".$taxonomy."! ".$term->get_error_message());
return array('id'=>false, 'slug'=>false);
}
$termId = $term['term_id'];
$term_slug = get_term($termId, $taxonomy)->slug; // Get the term slug
}
else
{
//Yes, grab it's id and slug
$termId = $term->term_id;
$term_slug = $term->slug;
}
return array('id'=>$termId, 'slug'=>$term_slug);
}
Okay finally now we can use the above to apply Attributes to our products.
For the main Variable Product:
$yourRawAttributeList = array("Gender" => array("Woman", "Man"), "Wheel Size" => array("28 Inch","30 Inch","32 Inch"));
$attribs = generate_attributes_list_for_product($yourRawAttributeList);
$p = new WC_Product_Variable($postID);
$p->set_props(array(
'attributes' => $attribs,
//Set any other properties of the product here you want - price, name, etc.
));
$postID = $p->save();
if ($postID <= 0) return "Unable to create / update product!";
//Attribute Terms: These need to be set otherwise the attributes dont show on the admin backend:
foreach ($attribs as $attrib)
{
/** @var WC_Product_Attribute $attrib */
$tax = $attrib->get_name();
$vals = $attrib->get_options();
$termsToAdd = array();
if (is_array($vals) && count($vals) > 0)
{
foreach ($vals as $val)
{
//Get or create the term if it doesnt exist:
$term = get_attribute_term($val, $tax);
if ($term['id']) $termsToAdd[] = $term['id'];
}
}
if (count($termsToAdd) > 0)
{
wp_set_object_terms($postID, $termsToAdd, $tax, true);
}
}
And for each Variation of that product:
//This is an array of input attributes in the form: array("Color"=>"Navy", "Size"=>"25")
$theseAttributes = array("Gender" => array("Woman"), "Wheel Size" => array("28 Inch"));
//This is the final list of attributes that we are calculating below.
$theseAttributesCalculated = array();
//logg("Want to add these attributes to the variation: ".print_r($theseAttributes, true));
$existingTax = wc_get_attribute_taxonomies();
foreach ($theseAttributes as $name => $value)
{
if (strlen($name) == 0 || strlen($value) == 0)
{
//logg("Attribute array had a blank value for product variant ".$sku.': '.print_r($theseAttributes, true));
return "Attribute array had a blank value.";
}
$tax = '';
$slug = '';
//Look for an existing taxonomy to match this attribute's $name
//$thistax->attribute_name = slug of the taxonomy
//$thistax->attribute_label = name of the taxonomy
foreach ($existingTax as $thistax)
{
if ($thistax->attribute_label == $name)
{
$slug = $thistax->attribute_name;
$tax = wc_attribute_taxonomy_name($slug);
break;
}
}
if (empty($tax))
{
$slug = wc_sanitize_taxonomy_name($name);
//Taxonomy not found, so create it...
if (create_global_attribute($name, $slug) > 0)
{
$tax = wc_attribute_taxonomy_name($slug);
}
else
{
//logg("Unable to create new attribute taxonomy ".$slug." for attribute ".$name."found in variable product ".$sku);
continue;
}
}
//logg("Want to add attribute ".$name. " value ".$value. " which is term ".$term_slug." (".$termId.") to post ".$parentID);
$term = get_attribute_term($value, $tax);
if ($term['id'])
{
// Set/save the attribute data in the product variation
$theseAttributesCalculated[$tax] = $term['slug'];
}
else
{
//logg("Warning! Unable to create / get the attribute ".$value." in Taxonomy ".$tax);
}
}
//logg("Finished gathering. Results: ".print_r($theseAttributesCalculated, true));
$variation = new \WC_Product_Variation($postID);
$variation->set_props( array(
'attributes' => $theseAttributesCalculated,
));
$postID = $variation->save();
if ($postID <= 0) return "Unable to create product variation!";
Upvotes: 17