Create programmatically a WooCommerce product variation with new attribute values - php
I have created a variable product ("parent" product) in WooCommerce version 3+. From a WordPress plugin, I would like to programmatically create the product variations ("children" product) with new attribute values.
The variation attributes are already set in WooCommerce.
So every time one variation is created, the new attribute's values should be created programmatically too and set in the parent Variable product.
How can this be done? is it possible?
Update : I had written more lines of code on this that I wished, and tried many things to solve it, using woocommerce objects, and added missing data about terms, termmeta, the relationship from term with post, in the database using the WordPress database object - but nothing has sufficed to make it work. And I couldn't pin-point where I went wrong - that is why I couldn't provide a narrower problem - things for which stackoverflow is more made for.
Update January 2020: Changed to WC_Product method get_name() instead of get_title()
Update September 2018: Handling taxonomy creation (Thanks to Carl F. Corneil)
From a defined variable product ID You will find below, a custom function that will add (create) a Product variation. The variable parent product needs to have set for it the needed attributes.
You will need to provide some information as:
the array of attributes/values
the Sku, prices and stock….
This data has to be stored in a formatted multi dimensional array (see an example at the end).
This function will check if the attributes values (term name) already exist and if not:
it create it for the product attribute
set it in the parent variable product.
The custom function code:
/**
* Create a product variation for a defined variable product ID.
*
* #since 3.0.0
* #param int $product_id | Post ID of the product parent variable product.
* #param array $variation_data | The data to insert in the product.
*/
function create_product_variation( $product_id, $variation_data ){
// Get the Variable product object (parent)
$product = wc_get_product($product_id);
$variation_post = array(
'post_title' => $product->get_name(),
'post_name' => 'product-'.$product_id.'-variation',
'post_status' => 'publish',
'post_parent' => $product_id,
'post_type' => 'product_variation',
'guid' => $product->get_permalink()
);
// Creating the product variation
$variation_id = wp_insert_post( $variation_post );
// Get an instance of the WC_Product_Variation object
$variation = new WC_Product_Variation( $variation_id );
// Iterating through the variations attributes
foreach ($variation_data['attributes'] as $attribute => $term_name )
{
$taxonomy = 'pa_'.$attribute; // The attribute taxonomy
// If taxonomy doesn't exists we create it (Thanks to Carl F. Corneil)
if( ! taxonomy_exists( $taxonomy ) ){
register_taxonomy(
$taxonomy,
'product_variation',
array(
'hierarchical' => false,
'label' => ucfirst( $attribute ),
'query_var' => true,
'rewrite' => array( 'slug' => sanitize_title($attribute) ), // The base slug
),
);
}
// Check if the Term name exist and if not we create it.
if( ! term_exists( $term_name, $taxonomy ) )
wp_insert_term( $term_name, $taxonomy ); // Create the term
$term_slug = get_term_by('name', $term_name, $taxonomy )->slug; // Get the term slug
// Get the post Terms names from the parent variable product.
$post_term_names = wp_get_post_terms( $product_id, $taxonomy, array('fields' => 'names') );
// Check if the post term exist and if not we set it in the parent variable product.
if( ! in_array( $term_name, $post_term_names ) )
wp_set_post_terms( $product_id, $term_name, $taxonomy, true );
// Set/save the attribute data in the product variation
update_post_meta( $variation_id, 'attribute_'.$taxonomy, $term_slug );
}
## Set/save all other data
// SKU
if( ! empty( $variation_data['sku'] ) )
$variation->set_sku( $variation_data['sku'] );
// Prices
if( empty( $variation_data['sale_price'] ) ){
$variation->set_price( $variation_data['regular_price'] );
} else {
$variation->set_price( $variation_data['sale_price'] );
$variation->set_sale_price( $variation_data['sale_price'] );
}
$variation->set_regular_price( $variation_data['regular_price'] );
// Stock
if( ! empty($variation_data['stock_qty']) ){
$variation->set_stock_quantity( $variation_data['stock_qty'] );
$variation->set_manage_stock(true);
$variation->set_stock_status('');
} else {
$variation->set_manage_stock(false);
}
$variation->set_weight(''); // weight (reseting)
$variation->save(); // Save the data
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Usage (example with 2 attributes):
$parent_id = 746; // Or get the variable product id dynamically
// The variation data
$variation_data = array(
'attributes' => array(
'size' => 'M',
'color' => 'Green',
),
'sku' => '',
'regular_price' => '22.00',
'sale_price' => '',
'stock_qty' => 10,
);
// The function to be run
create_product_variation( $parent_id, $variation_data );
Tested and works.
Part 2: Create programmatically a variable product and two new attributes in WooCommerce
You will get this in backend:
And it will work perfectly in front end.
Related: Create programmatically a product using CRUD methods in Woocommerce 3
I'm just going to throw this out there, since i couldn't get any of the above examples working. Don't ask me why as other people seem to have success. So, i took the minimalist approach and tried to figure out the bare essentials for a product attribute + variation (by creating it manually in wp and looking at the db) and came up with this.
$article_name = 'Test';
$post_id = wp_insert_post( array(
'post_author' => 1,
'post_title' => $article_name,
'post_content' => 'Lorem ipsum',
'post_status' => 'publish',
'post_type' => "product",
) );
wp_set_object_terms( $post_id, 'variable', 'product_type' );
$attr_label = 'Test attribute';
$attr_slug = sanitize_title($attr_label);
$attributes_array[$attr_slug] = array(
'name' => $attr_label,
'value' => 'alternative 1 | alternative 2',
'is_visible' => '1',
'is_variation' => '1',
'is_taxonomy' => '0' // for some reason, this is really important
);
update_post_meta( $post_id, '_product_attributes', $attributes_array );
$parent_id = $post_id;
$variation = array(
'post_title' => $article_name . ' (variation)',
'post_content' => '',
'post_status' => 'publish',
'post_parent' => $parent_id,
'post_type' => 'product_variation'
);
$variation_id = wp_insert_post( $variation );
update_post_meta( $variation_id, '_regular_price', 2 );
update_post_meta( $variation_id, '_price', 2 );
update_post_meta( $variation_id, '_stock_qty', 10 );
update_post_meta( $variation_id, 'attribute_' . $attr_slug, 'alternative 1' );
WC_Product_Variable::sync( $parent_id );
$variation_id = wp_insert_post( $variation );
update_post_meta( $variation_id, '_regular_price', 2 );
update_post_meta( $variation_id, '_price', 2 );
update_post_meta( $variation_id, '_stock_qty', 10 );
update_post_meta( $variation_id, 'attribute_' . $attr_slug, 'alternative 2' );
WC_Product_Variable::sync( $parent_id );
This is not using global product attributes, but article specific ones. Hope it helps someone as i was about ready to tear my hair out before i got it working.
EDIT: I'd say only use this if you can't get the official methods working. They change these things with time (field names such as '_regular_price" and so on) and chances are it's not super future proof to do it this way.
Not sure why, but none of these solutions worked for me, so I decided to create my own:
<?php
/**
* Create a variable product on woocommerce
* #return int Product ID
*/
function pricode_create_product(){
$product = new WC_Product_Variable();
$product->set_description('T-shirt variable description');
$product->set_name('T-shirt variable');
$product->set_sku('test-shirt');
$product->set_price(1);
$product->set_regular_price(1);
$product->set_stock_status();
$product->save();
return $product;
}
/**
* Create Product Attributes
* #param string $name Attribute name
* #param array $options Options values
* #return Object WC_Product_Attribute
*/
function pricode_create_attributes( $name, $options ){
$attribute = new WC_Product_Attribute();
$attribute->set_id(0);
$attribute->set_name($name);
$attribute->set_options($options);
$attribute->set_visible(true);
$attribute->set_variation(true);
return $attribute;
}
/**
* [pricode_create_variations description]
* #param [type] $product_id [description]
* #param [type] $values [description]
* #return [type] [description]
*/
function pricode_create_variations( $product_id, $values, $data ){
$variation = new WC_Product_Variation();
$variation->set_parent_id( $product_id );
$variation->set_attributes($values);
$variation->set_status('publish');
$variation->set_sku($data->sku);
$variation->set_price($data->price);
$variation->set_regular_price($data->price);
$variation->set_stock_status();
$variation->save();
$product = wc_get_product($product_id);
$product->save();
}
//Adding product
$product = pricode_create_product();
//Creating Attributes
$atts = [];
$atts[] = pricode_create_attributes('color',['red', 'green']);
$atts[] = pricode_create_attributes('size',['S', 'M']);
//Adding attributes to the created product
$product->set_attributes( $atts );
$product->save();
//Setting data (following Alexander's rec
$data = new stdClass();
$data->sku = 'sku-123';
$data->price = '10';
//Create variations
pricode_create_variations( $product->get_id(), ['color' => 'red', 'size' => 'M'], $data );
Hope it can help others.
Expanding on LoicTheAztec's answer, you can check if the attribute combination exists with the following modification to his code.
function create_update_product_variation( $product_id, $variation_data ){
if(isset($variation_data['variation_id'])) {
$variation_id = $variation_data['variation_id'];
} else {
// if the variation doesn't exist then create it
// Get the Variable product object (parent)
$product = wc_get_product($product_id);
$variation_post = array(
'post_title' => $product->get_title(),
'post_name' => 'product-'.$product_id.'-variation',
'post_status' => 'publish',
'post_parent' => $product_id,
'post_type' => 'product_variation',
'guid' => $product->get_permalink()
);
// Creating the product variation
$variation_id = wp_insert_post( $variation_post );
}
// ...
}
Example usage
// The variation data
$variation_data = array(
'attributes' => array(
'size' => 'M',
'color' => 'Green',
),
'sku' => '',
'regular_price' => '22.00',
'sale_price' => '1',
'stock_qty' => 1,
);
// check if variation exists
$meta_query = array();
foreach ($variation_data['attributes'] as $key => $value) {
$meta_query[] = array(
'key' => 'attribute_pa_' . $key,
'value' => $value
);
}
$variation_post = get_posts(array(
'post_type' => 'product_variation',
'numberposts' => 1,
'post_parent' => $parent_id,
'meta_query' => $meta_query
));
if($variation_post) {
$variation_data['variation_id'] = $variation_post[0]->ID;
}
create_update_product_variation( $product_id, $variation_data );
If you want to generate products for testing in WooCommerce, you can use WooCommerce Smooth Generator, made by WooCommerce itself.
https://github.com/woocommerce/wc-smooth-generator
Example:
// Generate WC_Product object and save it to database
// 70% change generated product is simple
// 30% chance generated product is variable
$product = \WC\SmoothGenerator\Generator\Product::generate();
// Returns WC_Product object of Simple product and don't save it to database
$product = \WC\SmoothGenerator\Generator\Product::generate_simple_product();
// Returns WC_Product object of Variable Product and saves it to database
$variable_product = \WC\SmoothGenerator\Generator\Product::generate_variable_product();
Src: https://github.com/woocommerce/wc-smooth-generator/blob/master/includes/Generator/Product.php
If you want to create products programatically, you can Product class with your needs.
it works but needs a little corrections (2 commas in the fuctions array), I did edit some of the code to my needs.
(working on wp 4.9 | wc 3.5)
First the products needs to have the attribute already created and associated, my taxonomy is "pa_r" friendly name "R"
backend attribute associeated img
the function with the correction
function create_product_variation( $product_id, $variation_data ){
// Get the Variable product object (parent)
$product = wc_get_product($product_id);
$variation_post = array(
'post_title' => $product->get_title(),
'post_name' => 'product-'.$product_id.'-variation',
'post_status' => 'publish',
'post_parent' => $product_id,
'post_type' => 'product_variation',
'guid' => $product->get_permalink()
);
// Creating the product variation
$variation_id = wp_insert_post( $variation_post );
// Get an instance of the WC_Product_Variation object
$variation = new WC_Product_Variation( $variation_id );
// Iterating through the variations attributes
foreach ($variation_data['attributes'] as $attribute => $term_name )
{
$taxonomy = 'pa_'.$attribute; // The attribute taxonomy
// If taxonomy doesn't exists we create it (Thanks to Carl F. Corneil)
if( ! taxonomy_exists( $taxonomy ) ){
register_taxonomy(
$taxonomy,
'product_variation',
array(
'hierarchical' => false,
'label' => ucfirst( $taxonomy ),
'query_var' => true,
'rewrite' => array( 'slug' => '$taxonomy') // The base slug
)
);
}
// Check if the Term name exist and if not we create it.
if( ! term_exists( $term_name, $taxonomy ) )
wp_insert_term( $term_name, $taxonomy ); // Create the term
$term_slug = get_term_by('name', $term_name, $taxonomy )->slug; // Get the term slug
// Get the post Terms names from the parent variable product.
$post_term_names = wp_get_post_terms( $product_id, $taxonomy, array('fields' => 'names') );
// Check if the post term exist and if not we set it in the parent variable product.
if( ! in_array( $term_name, $post_term_names ) )
wp_set_post_terms( $product_id, $term_name, $taxonomy, true );
// Set/save the attribute data in the product variation
update_post_meta( $variation_id, 'attribute_'.$taxonomy, $term_slug );
}
## Set/save all other data
// SKU
if( ! empty( $variation_data['sku'] ) )
$variation->set_sku( $variation_data['sku'] );
// Prices
if( empty( $variation_data['sale_price'] ) ){
$variation->set_price( $variation_data['regular_price'] );
} else {
$variation->set_price( $variation_data['sale_price'] );
$variation->set_sale_price( $variation_data['sale_price'] );
}
$variation->set_regular_price( $variation_data['regular_price'] );
// Stock
if( ! empty($variation_data['stock_qty']) ){
$variation->set_stock_quantity( $variation_data['stock_qty'] );
$variation->set_manage_stock(true);
$variation->set_stock_status('');
} else {
$variation->set_manage_stock(false);
}
$variation->set_weight(''); // weight (reseting)
$variation->save(); // Save the data
}
I made an array with the data I need in the variations [id_post, attribute, sku, regular_price, stock]
$hijos = array(
[9623,'265/70 R16','NE-CT-CO-USA-016-005','0',53],
[9624,'235/65 R17','NE-AU-CO-EUR-017-050','189000',56]
);
and the foreach to create dynamically all the variations to my products
foreach ($hijos as $vari) {
// The variation data
$variation_data = array(
'attributes' => array(
'r' => $vari[1],
),
'sku' => $vari[2],
'regular_price' => str_replace('.', '', $vari[3]),
'stock_qty' => $vari[4]
);
// var_dump($variation_data);
create_product_variation( $vari[0], $variation_data );
}
You might run into a problem here if the the taxonomy of the product is not registered beforehand somewhere else. If you want to make sure the taxonomy exists, you could add a conditional to LoicTheAztec's answer.
Something like this.
foreach ($variation_data['attributes'] as $attribute => $term_name )
{
//echo 'attribute ' . $attribute . ' term name ' . $term_name;
$taxonomy = 'pa_' . $attribute; // The attribute taxonomy
// Check if the Taxonomy exists, and if not we create it.
if (! taxonomy_exists($taxonomy)){
register_taxonomy(
$taxonomy, //The name of the taxonomy. Name should be in slug form (must not contain capital letters or spaces).
'product', //post type name
array(
'hierarchical' => false,
'label' => ucfirst($taxonomy), //Display name
'query_var' => true,
'rewrite' => array(
'slug' => $taxonomy, // This controls the base slug that will display before each term
'with_front' => false // Don't display the category base before
),
)
);
}
...
above answer (LoicTheAztec) helped me a lot but there was some issues instead of using
update_post_meta( $variation_id, 'attribute_'.$taxonomy, $term_slug );
use :
update_post_meta( $variation_id, 'attribute_'.$attribute, $term_name );
because taxonomy is already is been modified and this is causing problem to update post meta after changing this update values and not selecting automatically in admin product variation edit after this changes it work great !
In addition to Alejandro Giraldo answer to use taxonomies instead of custom product attributes you can use this modified version
<?php
/**
* Create a variable product on woocommerce
* #return int Product ID
*/
function pricode_create_product(){
$product = new WC_Product_Variable();
$product->set_description('T-shirt variable description');
$product->set_name('T-shirt variable');
$product->set_sku('test-shirt');
$product->set_price(1);
$product->set_regular_price(1);
$product->set_stock_status();
return $product->save();
}
/**
* Create Product Attributes
* #param string $name Attribute name
* #param array $options Options values
* #return Object WC_Product_Attribute
*/
function pricode_create_attributes( $name, $options ){
$attributes = array();
if(!empty($options)){
if(is_string($options)){
$term = wp_create_term(
$options,
wc_attribute_taxonomy_name($name)
);
if(is_array($term)){
$attributes[] = (int)$term['term_id'];
}
}
else{
for($i = 0; $i < count($options); $i++){
$term = wp_create_term(
$options[$i],
wc_attribute_taxonomy_name($name)
);
if(is_array($term)){
$attributes[] = (int)$term['term_id'];
}
}
}
}
$attribute = new WC_Product_Attribute();
/*
Set the attribute id to the id of the taxonomy to use
with wc_attribute_taxonomy_id_by_name you get the id of the taxonomy stored in {$wpdb->prefix}woocommerce_attribute_taxonomies
with wc_attribute_taxonomy_name you convert the Attribute name to the attribute name woocommerce use
#see https://woocommerce.github.io/code-reference/namespaces/default.html#function_wc_attribute_taxonomy_id_by_name
#see https://woocommerce.github.io/code-reference/namespaces/default.html#function_wc_attribute_taxonomy_name
/*
$attribute->set_id(wc_attribute_taxonomy_id_by_name(wc_attribute_taxonomy_name($name)));
$attribute->set_name(wc_attribute_taxonomy_name($name));
$attribute->set_options($attributes);
$attribute->set_visible(true);
$attribute->set_variation(true);
return $attribute;
}
/**
* [pricode_create_variations description]
* #param [type] $product_id [description]
* #param [type] $values [description]
* #return [type] [description]
*/
function pricode_create_variations( $product_id, $values ){
$variation = new WC_Product_Variation();
$variation->set_parent_id( $product_id );
$variation->set_attributes($values);
$variation->set_status('publish');
$variation->set_sku($data->sku);
$variation->set_price($data->price);
$variation->set_regular_price($data->price);
$variation->set_stock_status();
$variation->save();
$product = wc_get_product($product_id);
$product->save();
}
//Adding product
$product = pricode_create_product();
//Creating Attributes
$atts = [];
$atts[] = pricode_create_attributes('color',['red', 'green']);
$atts[] = pricode_create_attributes('size',['S', 'M']);
//Adding attributes to the created product
$product->set_attributes( $atts );
$product->save();
//Create variations
pricode_create_variations( $product->get_id(), [wc_attribute_taxonomy_name('color') => sanitize_title('red'), wc_attribute_taxonomy_name('size') => sanitize_title('M')]);
Coming in late to the party but adding to the answer from LoicTheAztec (which works perfectly) if your new variation does not end up with a selected attribute, use the following line just before the save method:
$variation->set_weight(''); //LoicTheAztec
$variation->set_attributes($variation_data["attributes"]); // Select the attributes
$variation->save(); //LoicTheAztec
The accepted article works like a charm. If you manage to create the variations but it comes out without an option selected in the variation and when you try to save a single one resets to empty, I highly suggest checking this thread Creating WooCommerce product variation adds an empty attribute value , I ve been struggling with the same issue for over 2 hours. Happy coding
This is the write way to create variable product
// Name and image would be enough
$product = new WC_Product_Variable();
$product->set_name( 'Wizard Hat' );
$product->set_image_id( 90 );
// one available for variation attribute
$attribute = new WC_Product_Attribute();
$attribute->set_name( 'Magical' );
$attribute->set_options( array( 'Yes', 'No' ) );
$attribute->set_position( 0 );
$attribute->set_visible( true );
$attribute->set_variation( true ); // here it is
$product->set_attributes( array( $attribute ) );
// save the changes and go on
$product->save();
// now we need two variations for Magical and Non-magical Wizard hat
$variation = new WC_Product_Variation();
$variation->set_parent_id( $product->get_id() );
$variation->set_attributes( array( 'magical' => 'Yes' ) );
$variation->set_regular_price( 1000000 ); // yep, magic hat is quite expensive
$variation->save();
$variation = new WC_Product_Variation();
$variation->set_parent_id( $product->get_id() );
$variation->set_attributes( array( 'magical' => 'No' ) );
$variation->set_regular_price( 500 );
$variation->save();
Related
Can't choose variations for WooCommerce product created programmatically
I am using a custom PHP Script to create variations of a WooCommerce product. The user chooses "Size" and "Color" of the product through a select field, and the variations is created based on that. Works fine, product is created with the correct price, attribute and variations. Except that you cant select the actual variations when trying to shop (they are "disabled"). When I go back-end in WooCommerce and open the product there, and hit the blue "Update"-button on the product, it works as intended and the variations can be selected and bought front-end. Is there any function to "force update" the product in PHP after variations have been created? -- Below the code which adds color and size as attributes to the product. Also creates a single variation with "Any color" and "Any size" as the rules. function save_wc_custom_attributes($post_id, $custom_attributes) { $i = 0; $product = wc_get_product( $post_id ); $product_id = $post_id; $terms = wp_get_post_terms( $post_id, 'pa_farge') ; // Remove existing colors from the product foreach ( $terms as $term ) { wp_remove_object_terms( $post_id, $term->term_id, 'pa_farge' ); } $terms = wp_get_post_terms( $post_id, 'pa_str') ; // Remove existing sizes from the product foreach ( $terms as $term ) { wp_remove_object_terms( $post_id, $term->term_id, 'pa_str' ); } foreach ($custom_attributes as $name => $value) { // Relate post to a custom attribute, add term if it does not exist wp_set_object_terms($post_id, $value, $name, true); // Create product attributes array $product_attributes[$i] = array( 'name' => $name, // set attribute name 'value' => $value, // set attribute value 'is_visible' => 1, 'is_variation' => 1, 'is_taxonomy' => 1 ); $i++; } // Now update the post with its new attributes update_post_meta($post_id, '_product_attributes', $product_attributes); // Details for the variation $variation_post = array( 'post_title' => $product->get_name(), 'post_name' => 'product-'.$product_id.'-variation', 'post_status' => 'publish', 'post_parent' => $product_id, 'post_type' => 'product_variation', 'guid' => $product->get_permalink() ); if( !$product->has_child() ) { // Creating a single variation if none exists $variation_id = wp_insert_post( $variation_post ); $variation = new WC_Product_Variation( $variation_id ); $variation->set_regular_price( $product->get_price() ); $variation->save(); // Save the data $product_variable = new WC_Product_Variable($post_id); $product_variable->sync($post_id); wc_delete_product_transients($post_id); } else { // Update the variation if it already exists $product_variable = new WC_Product_Variable($post_id); $product_variable->sync($post_id); wc_delete_product_transients($post_id); } $product->save(); }
Your actual code is jus setting product attributes to the main variable product, but it doesn't set any attribute to a product variation… So the question is may be related to this variable product instead… So try simply to add at the end of your code: $product = wc_get_product( $post_id ); $product->save();
How to programmatically set a term to a Woocommerce product variation?
I would like to set all variable products, when created, with the same six different variations using an already defined attribute ("bidragsstorlek"). The six variations should have different prices. With my current code I succeded with all steps except the last one. I succefully added an attribute, six diffrent terms and six different variations. When I look in the back end panel the product variations have the prices I want, the only thing missing is to set the right term to each variation. I know similar questions been asked before and been trying to implement those answers, but can't get it right. $taxonomy = 'pa_bidragsstorlek'; $arrtermnames = array('50 kronor', '100 kronor', '250 kronor', '500 kronor', '1000 kronor', '5000 kronor'); $attr_data = Array( $taxonomy=>Array( 'name' => $taxonomy, 'value' => '', 'is_visible' => '1', 'is_variation' => '1', 'is_taxonomy' => '1' ) ); $newproduct = new WC_Product($product_id); update_post_meta( $product_id, '_product_attributes', array_merge($newproduct->get_attributes(), $attr_data) ); foreach ($arrtermnames as &$value) { $term_name = $value; // The term "NAME" $term_slug = sanitize_title($term_name); // The term "slug" // Check if the term exist and if not it create it (and get the term ID). if( ! term_exists( $term_name, $taxonomy ) ){ $term_data = wp_insert_term( $term_name, $taxonomy ); $term_id = $term_data['term_id']; } else { $term_id = get_term_by( 'name', $term_name, $taxonomy )->term_id; } // get an instance of the WC_Product Object $product = wc_get_product( $product_id ); $attributes = (array) $product->get_attributes(); // 1. If the product attribute is set for the product if( array_key_exists( $taxonomy, $attributes ) ) { foreach( $attributes as $key => $attribute ){ if( $key == $taxonomy ){ $options = (array) $attribute->get_options(); $options[] = $term_id; $attribute->set_options($options); $attributes[$key] = $attribute; break; } } $product->set_attributes( $attributes ); } // 2. The product attribute is not set for the product else { $attribute = new WC_Product_Attribute(); $attribute->set_id( sizeof( $attributes) + 1 ); $attribute->set_name( $taxonomy ); $attribute->set_options( array( $term_id ) ); $attribute->set_position( sizeof( $attributes) + 1 ); $attribute->set_visible( true ); $attribute->set_variation( false ); $attributes[] = $attribute; $product->set_attributes( $attributes ); } $product->save(); // Append the new term in the product if( ! has_term( $term_name, $taxonomy, $product_id )) wp_set_object_terms($product_id, $term_slug, $taxonomy, true ); } unset($value); $parent_id = $product_id; $arrprice = array(50, 100, 250, 500, 1000, 5000); for ($x = 0; $x <= 5; $x++) { $variation = array( 'post_title' => 'Product #' . $parent_id. $arrprice[$x], 'post_content' => '', 'post_status' => 'publish', 'post_parent' => $parent_id, 'post_type' => 'product_variation' ); // The variation id $variation_id = wp_insert_post( $variation ); update_post_meta( $variation_id, '_regular_price', $arrprice[$x] ); update_post_meta( $variation_id, '_price', $arrtermnames[$x] ); update_post_meta( $variation_id, '_virtual', '1' ); // Assign the size and color of this variation update_post_meta( $variation_id, 'attribute_'.$taxonomy, $arrtermnames[$x] ); WC_Product_Variable::sync( $parent_id ); } Right now with this code, when checking in the panel, I have a drop-down at each variation with all the terms - but none is choosen. Any idea what is missing to make each variation to be set to the right term?
How to add Woocommerce product variations programmatically? [duplicate]
I have created a variable product ("parent" product) in WooCommerce version 3+. From a WordPress plugin, I would like to programmatically create the product variations ("children" product) with new attribute values. The variation attributes are already set in WooCommerce. So every time one variation is created, the new attribute's values should be created programmatically too and set in the parent Variable product. How can this be done? is it possible? Update : I had written more lines of code on this that I wished, and tried many things to solve it, using woocommerce objects, and added missing data about terms, termmeta, the relationship from term with post, in the database using the WordPress database object - but nothing has sufficed to make it work. And I couldn't pin-point where I went wrong - that is why I couldn't provide a narrower problem - things for which stackoverflow is more made for.
Update January 2020: Changed to WC_Product method get_name() instead of get_title() Update September 2018: Handling taxonomy creation (Thanks to Carl F. Corneil) From a defined variable product ID You will find below, a custom function that will add (create) a Product variation. The variable parent product needs to have set for it the needed attributes. You will need to provide some information as: the array of attributes/values the Sku, prices and stock…. This data has to be stored in a formatted multi dimensional array (see an example at the end). This function will check if the attributes values (term name) already exist and if not: it create it for the product attribute set it in the parent variable product. The custom function code: /** * Create a product variation for a defined variable product ID. * * #since 3.0.0 * #param int $product_id | Post ID of the product parent variable product. * #param array $variation_data | The data to insert in the product. */ function create_product_variation( $product_id, $variation_data ){ // Get the Variable product object (parent) $product = wc_get_product($product_id); $variation_post = array( 'post_title' => $product->get_name(), 'post_name' => 'product-'.$product_id.'-variation', 'post_status' => 'publish', 'post_parent' => $product_id, 'post_type' => 'product_variation', 'guid' => $product->get_permalink() ); // Creating the product variation $variation_id = wp_insert_post( $variation_post ); // Get an instance of the WC_Product_Variation object $variation = new WC_Product_Variation( $variation_id ); // Iterating through the variations attributes foreach ($variation_data['attributes'] as $attribute => $term_name ) { $taxonomy = 'pa_'.$attribute; // The attribute taxonomy // If taxonomy doesn't exists we create it (Thanks to Carl F. Corneil) if( ! taxonomy_exists( $taxonomy ) ){ register_taxonomy( $taxonomy, 'product_variation', array( 'hierarchical' => false, 'label' => ucfirst( $attribute ), 'query_var' => true, 'rewrite' => array( 'slug' => sanitize_title($attribute) ), // The base slug ), ); } // Check if the Term name exist and if not we create it. if( ! term_exists( $term_name, $taxonomy ) ) wp_insert_term( $term_name, $taxonomy ); // Create the term $term_slug = get_term_by('name', $term_name, $taxonomy )->slug; // Get the term slug // Get the post Terms names from the parent variable product. $post_term_names = wp_get_post_terms( $product_id, $taxonomy, array('fields' => 'names') ); // Check if the post term exist and if not we set it in the parent variable product. if( ! in_array( $term_name, $post_term_names ) ) wp_set_post_terms( $product_id, $term_name, $taxonomy, true ); // Set/save the attribute data in the product variation update_post_meta( $variation_id, 'attribute_'.$taxonomy, $term_slug ); } ## Set/save all other data // SKU if( ! empty( $variation_data['sku'] ) ) $variation->set_sku( $variation_data['sku'] ); // Prices if( empty( $variation_data['sale_price'] ) ){ $variation->set_price( $variation_data['regular_price'] ); } else { $variation->set_price( $variation_data['sale_price'] ); $variation->set_sale_price( $variation_data['sale_price'] ); } $variation->set_regular_price( $variation_data['regular_price'] ); // Stock if( ! empty($variation_data['stock_qty']) ){ $variation->set_stock_quantity( $variation_data['stock_qty'] ); $variation->set_manage_stock(true); $variation->set_stock_status(''); } else { $variation->set_manage_stock(false); } $variation->set_weight(''); // weight (reseting) $variation->save(); // Save the data } Code goes in function.php file of your active child theme (or theme) or also in any plugin file. Usage (example with 2 attributes): $parent_id = 746; // Or get the variable product id dynamically // The variation data $variation_data = array( 'attributes' => array( 'size' => 'M', 'color' => 'Green', ), 'sku' => '', 'regular_price' => '22.00', 'sale_price' => '', 'stock_qty' => 10, ); // The function to be run create_product_variation( $parent_id, $variation_data ); Tested and works. Part 2: Create programmatically a variable product and two new attributes in WooCommerce You will get this in backend: And it will work perfectly in front end. Related: Create programmatically a product using CRUD methods in Woocommerce 3
I'm just going to throw this out there, since i couldn't get any of the above examples working. Don't ask me why as other people seem to have success. So, i took the minimalist approach and tried to figure out the bare essentials for a product attribute + variation (by creating it manually in wp and looking at the db) and came up with this. $article_name = 'Test'; $post_id = wp_insert_post( array( 'post_author' => 1, 'post_title' => $article_name, 'post_content' => 'Lorem ipsum', 'post_status' => 'publish', 'post_type' => "product", ) ); wp_set_object_terms( $post_id, 'variable', 'product_type' ); $attr_label = 'Test attribute'; $attr_slug = sanitize_title($attr_label); $attributes_array[$attr_slug] = array( 'name' => $attr_label, 'value' => 'alternative 1 | alternative 2', 'is_visible' => '1', 'is_variation' => '1', 'is_taxonomy' => '0' // for some reason, this is really important ); update_post_meta( $post_id, '_product_attributes', $attributes_array ); $parent_id = $post_id; $variation = array( 'post_title' => $article_name . ' (variation)', 'post_content' => '', 'post_status' => 'publish', 'post_parent' => $parent_id, 'post_type' => 'product_variation' ); $variation_id = wp_insert_post( $variation ); update_post_meta( $variation_id, '_regular_price', 2 ); update_post_meta( $variation_id, '_price', 2 ); update_post_meta( $variation_id, '_stock_qty', 10 ); update_post_meta( $variation_id, 'attribute_' . $attr_slug, 'alternative 1' ); WC_Product_Variable::sync( $parent_id ); $variation_id = wp_insert_post( $variation ); update_post_meta( $variation_id, '_regular_price', 2 ); update_post_meta( $variation_id, '_price', 2 ); update_post_meta( $variation_id, '_stock_qty', 10 ); update_post_meta( $variation_id, 'attribute_' . $attr_slug, 'alternative 2' ); WC_Product_Variable::sync( $parent_id ); This is not using global product attributes, but article specific ones. Hope it helps someone as i was about ready to tear my hair out before i got it working. EDIT: I'd say only use this if you can't get the official methods working. They change these things with time (field names such as '_regular_price" and so on) and chances are it's not super future proof to do it this way.
Not sure why, but none of these solutions worked for me, so I decided to create my own: <?php /** * Create a variable product on woocommerce * #return int Product ID */ function pricode_create_product(){ $product = new WC_Product_Variable(); $product->set_description('T-shirt variable description'); $product->set_name('T-shirt variable'); $product->set_sku('test-shirt'); $product->set_price(1); $product->set_regular_price(1); $product->set_stock_status(); $product->save(); return $product; } /** * Create Product Attributes * #param string $name Attribute name * #param array $options Options values * #return Object WC_Product_Attribute */ function pricode_create_attributes( $name, $options ){ $attribute = new WC_Product_Attribute(); $attribute->set_id(0); $attribute->set_name($name); $attribute->set_options($options); $attribute->set_visible(true); $attribute->set_variation(true); return $attribute; } /** * [pricode_create_variations description] * #param [type] $product_id [description] * #param [type] $values [description] * #return [type] [description] */ function pricode_create_variations( $product_id, $values, $data ){ $variation = new WC_Product_Variation(); $variation->set_parent_id( $product_id ); $variation->set_attributes($values); $variation->set_status('publish'); $variation->set_sku($data->sku); $variation->set_price($data->price); $variation->set_regular_price($data->price); $variation->set_stock_status(); $variation->save(); $product = wc_get_product($product_id); $product->save(); } //Adding product $product = pricode_create_product(); //Creating Attributes $atts = []; $atts[] = pricode_create_attributes('color',['red', 'green']); $atts[] = pricode_create_attributes('size',['S', 'M']); //Adding attributes to the created product $product->set_attributes( $atts ); $product->save(); //Setting data (following Alexander's rec $data = new stdClass(); $data->sku = 'sku-123'; $data->price = '10'; //Create variations pricode_create_variations( $product->get_id(), ['color' => 'red', 'size' => 'M'], $data ); Hope it can help others.
Expanding on LoicTheAztec's answer, you can check if the attribute combination exists with the following modification to his code. function create_update_product_variation( $product_id, $variation_data ){ if(isset($variation_data['variation_id'])) { $variation_id = $variation_data['variation_id']; } else { // if the variation doesn't exist then create it // Get the Variable product object (parent) $product = wc_get_product($product_id); $variation_post = array( 'post_title' => $product->get_title(), 'post_name' => 'product-'.$product_id.'-variation', 'post_status' => 'publish', 'post_parent' => $product_id, 'post_type' => 'product_variation', 'guid' => $product->get_permalink() ); // Creating the product variation $variation_id = wp_insert_post( $variation_post ); } // ... } Example usage // The variation data $variation_data = array( 'attributes' => array( 'size' => 'M', 'color' => 'Green', ), 'sku' => '', 'regular_price' => '22.00', 'sale_price' => '1', 'stock_qty' => 1, ); // check if variation exists $meta_query = array(); foreach ($variation_data['attributes'] as $key => $value) { $meta_query[] = array( 'key' => 'attribute_pa_' . $key, 'value' => $value ); } $variation_post = get_posts(array( 'post_type' => 'product_variation', 'numberposts' => 1, 'post_parent' => $parent_id, 'meta_query' => $meta_query )); if($variation_post) { $variation_data['variation_id'] = $variation_post[0]->ID; } create_update_product_variation( $product_id, $variation_data );
If you want to generate products for testing in WooCommerce, you can use WooCommerce Smooth Generator, made by WooCommerce itself. https://github.com/woocommerce/wc-smooth-generator Example: // Generate WC_Product object and save it to database // 70% change generated product is simple // 30% chance generated product is variable $product = \WC\SmoothGenerator\Generator\Product::generate(); // Returns WC_Product object of Simple product and don't save it to database $product = \WC\SmoothGenerator\Generator\Product::generate_simple_product(); // Returns WC_Product object of Variable Product and saves it to database $variable_product = \WC\SmoothGenerator\Generator\Product::generate_variable_product(); Src: https://github.com/woocommerce/wc-smooth-generator/blob/master/includes/Generator/Product.php If you want to create products programatically, you can Product class with your needs.
it works but needs a little corrections (2 commas in the fuctions array), I did edit some of the code to my needs. (working on wp 4.9 | wc 3.5) First the products needs to have the attribute already created and associated, my taxonomy is "pa_r" friendly name "R" backend attribute associeated img the function with the correction function create_product_variation( $product_id, $variation_data ){ // Get the Variable product object (parent) $product = wc_get_product($product_id); $variation_post = array( 'post_title' => $product->get_title(), 'post_name' => 'product-'.$product_id.'-variation', 'post_status' => 'publish', 'post_parent' => $product_id, 'post_type' => 'product_variation', 'guid' => $product->get_permalink() ); // Creating the product variation $variation_id = wp_insert_post( $variation_post ); // Get an instance of the WC_Product_Variation object $variation = new WC_Product_Variation( $variation_id ); // Iterating through the variations attributes foreach ($variation_data['attributes'] as $attribute => $term_name ) { $taxonomy = 'pa_'.$attribute; // The attribute taxonomy // If taxonomy doesn't exists we create it (Thanks to Carl F. Corneil) if( ! taxonomy_exists( $taxonomy ) ){ register_taxonomy( $taxonomy, 'product_variation', array( 'hierarchical' => false, 'label' => ucfirst( $taxonomy ), 'query_var' => true, 'rewrite' => array( 'slug' => '$taxonomy') // The base slug ) ); } // Check if the Term name exist and if not we create it. if( ! term_exists( $term_name, $taxonomy ) ) wp_insert_term( $term_name, $taxonomy ); // Create the term $term_slug = get_term_by('name', $term_name, $taxonomy )->slug; // Get the term slug // Get the post Terms names from the parent variable product. $post_term_names = wp_get_post_terms( $product_id, $taxonomy, array('fields' => 'names') ); // Check if the post term exist and if not we set it in the parent variable product. if( ! in_array( $term_name, $post_term_names ) ) wp_set_post_terms( $product_id, $term_name, $taxonomy, true ); // Set/save the attribute data in the product variation update_post_meta( $variation_id, 'attribute_'.$taxonomy, $term_slug ); } ## Set/save all other data // SKU if( ! empty( $variation_data['sku'] ) ) $variation->set_sku( $variation_data['sku'] ); // Prices if( empty( $variation_data['sale_price'] ) ){ $variation->set_price( $variation_data['regular_price'] ); } else { $variation->set_price( $variation_data['sale_price'] ); $variation->set_sale_price( $variation_data['sale_price'] ); } $variation->set_regular_price( $variation_data['regular_price'] ); // Stock if( ! empty($variation_data['stock_qty']) ){ $variation->set_stock_quantity( $variation_data['stock_qty'] ); $variation->set_manage_stock(true); $variation->set_stock_status(''); } else { $variation->set_manage_stock(false); } $variation->set_weight(''); // weight (reseting) $variation->save(); // Save the data } I made an array with the data I need in the variations [id_post, attribute, sku, regular_price, stock] $hijos = array( [9623,'265/70 R16','NE-CT-CO-USA-016-005','0',53], [9624,'235/65 R17','NE-AU-CO-EUR-017-050','189000',56] ); and the foreach to create dynamically all the variations to my products foreach ($hijos as $vari) { // The variation data $variation_data = array( 'attributes' => array( 'r' => $vari[1], ), 'sku' => $vari[2], 'regular_price' => str_replace('.', '', $vari[3]), 'stock_qty' => $vari[4] ); // var_dump($variation_data); create_product_variation( $vari[0], $variation_data ); }
You might run into a problem here if the the taxonomy of the product is not registered beforehand somewhere else. If you want to make sure the taxonomy exists, you could add a conditional to LoicTheAztec's answer. Something like this. foreach ($variation_data['attributes'] as $attribute => $term_name ) { //echo 'attribute ' . $attribute . ' term name ' . $term_name; $taxonomy = 'pa_' . $attribute; // The attribute taxonomy // Check if the Taxonomy exists, and if not we create it. if (! taxonomy_exists($taxonomy)){ register_taxonomy( $taxonomy, //The name of the taxonomy. Name should be in slug form (must not contain capital letters or spaces). 'product', //post type name array( 'hierarchical' => false, 'label' => ucfirst($taxonomy), //Display name 'query_var' => true, 'rewrite' => array( 'slug' => $taxonomy, // This controls the base slug that will display before each term 'with_front' => false // Don't display the category base before ), ) ); } ...
above answer (LoicTheAztec) helped me a lot but there was some issues instead of using update_post_meta( $variation_id, 'attribute_'.$taxonomy, $term_slug ); use : update_post_meta( $variation_id, 'attribute_'.$attribute, $term_name ); because taxonomy is already is been modified and this is causing problem to update post meta after changing this update values and not selecting automatically in admin product variation edit after this changes it work great !
In addition to Alejandro Giraldo answer to use taxonomies instead of custom product attributes you can use this modified version <?php /** * Create a variable product on woocommerce * #return int Product ID */ function pricode_create_product(){ $product = new WC_Product_Variable(); $product->set_description('T-shirt variable description'); $product->set_name('T-shirt variable'); $product->set_sku('test-shirt'); $product->set_price(1); $product->set_regular_price(1); $product->set_stock_status(); return $product->save(); } /** * Create Product Attributes * #param string $name Attribute name * #param array $options Options values * #return Object WC_Product_Attribute */ function pricode_create_attributes( $name, $options ){ $attributes = array(); if(!empty($options)){ if(is_string($options)){ $term = wp_create_term( $options, wc_attribute_taxonomy_name($name) ); if(is_array($term)){ $attributes[] = (int)$term['term_id']; } } else{ for($i = 0; $i < count($options); $i++){ $term = wp_create_term( $options[$i], wc_attribute_taxonomy_name($name) ); if(is_array($term)){ $attributes[] = (int)$term['term_id']; } } } } $attribute = new WC_Product_Attribute(); /* Set the attribute id to the id of the taxonomy to use with wc_attribute_taxonomy_id_by_name you get the id of the taxonomy stored in {$wpdb->prefix}woocommerce_attribute_taxonomies with wc_attribute_taxonomy_name you convert the Attribute name to the attribute name woocommerce use #see https://woocommerce.github.io/code-reference/namespaces/default.html#function_wc_attribute_taxonomy_id_by_name #see https://woocommerce.github.io/code-reference/namespaces/default.html#function_wc_attribute_taxonomy_name /* $attribute->set_id(wc_attribute_taxonomy_id_by_name(wc_attribute_taxonomy_name($name))); $attribute->set_name(wc_attribute_taxonomy_name($name)); $attribute->set_options($attributes); $attribute->set_visible(true); $attribute->set_variation(true); return $attribute; } /** * [pricode_create_variations description] * #param [type] $product_id [description] * #param [type] $values [description] * #return [type] [description] */ function pricode_create_variations( $product_id, $values ){ $variation = new WC_Product_Variation(); $variation->set_parent_id( $product_id ); $variation->set_attributes($values); $variation->set_status('publish'); $variation->set_sku($data->sku); $variation->set_price($data->price); $variation->set_regular_price($data->price); $variation->set_stock_status(); $variation->save(); $product = wc_get_product($product_id); $product->save(); } //Adding product $product = pricode_create_product(); //Creating Attributes $atts = []; $atts[] = pricode_create_attributes('color',['red', 'green']); $atts[] = pricode_create_attributes('size',['S', 'M']); //Adding attributes to the created product $product->set_attributes( $atts ); $product->save(); //Create variations pricode_create_variations( $product->get_id(), [wc_attribute_taxonomy_name('color') => sanitize_title('red'), wc_attribute_taxonomy_name('size') => sanitize_title('M')]);
Coming in late to the party but adding to the answer from LoicTheAztec (which works perfectly) if your new variation does not end up with a selected attribute, use the following line just before the save method: $variation->set_weight(''); //LoicTheAztec $variation->set_attributes($variation_data["attributes"]); // Select the attributes $variation->save(); //LoicTheAztec
The accepted article works like a charm. If you manage to create the variations but it comes out without an option selected in the variation and when you try to save a single one resets to empty, I highly suggest checking this thread Creating WooCommerce product variation adds an empty attribute value , I ve been struggling with the same issue for over 2 hours. Happy coding
This is the write way to create variable product // Name and image would be enough $product = new WC_Product_Variable(); $product->set_name( 'Wizard Hat' ); $product->set_image_id( 90 ); // one available for variation attribute $attribute = new WC_Product_Attribute(); $attribute->set_name( 'Magical' ); $attribute->set_options( array( 'Yes', 'No' ) ); $attribute->set_position( 0 ); $attribute->set_visible( true ); $attribute->set_variation( true ); // here it is $product->set_attributes( array( $attribute ) ); // save the changes and go on $product->save(); // now we need two variations for Magical and Non-magical Wizard hat $variation = new WC_Product_Variation(); $variation->set_parent_id( $product->get_id() ); $variation->set_attributes( array( 'magical' => 'Yes' ) ); $variation->set_regular_price( 1000000 ); // yep, magic hat is quite expensive $variation->save(); $variation = new WC_Product_Variation(); $variation->set_parent_id( $product->get_id() ); $variation->set_attributes( array( 'magical' => 'No' ) ); $variation->set_regular_price( 500 ); $variation->save();
How to get Woocommerce Variation ID?
I've already had this code on functions.php This code is to add a new field on each product variation to have datetime input field, so variations would automatically expired when the current date is past the input date. // Add Variation Settings add_action( 'woocommerce_product_after_variable_attributes', 'variation_settings_fields', 10, 3 ); // Save Variation Settings add_action( 'woocommerce_save_product_variation', 'save_variation_settings_fields', 10, 2 ); //Create New fields for Variations function variation_settings_fields( $loop, $variation_data, $variation ) { // Text Field woocommerce_wp_text_input( array( 'id' => '_text_field_date_expire[' . $variation->ID . ']', 'class' => '_text_field_date_expire', 'type' => 'datetime-local', 'label' => __( 'Date of Expiration', 'woocommerce' ), 'placeholder' => '', 'desc_tip' => 'true', 'description' => __( 'Expiration of Each Product Variation', 'woocommerce' ), 'value' => get_post_meta( $variation->ID, '_text_field_date_expire', true ) ) ); } //Save New Fields for Variation function save_variation_settings_fields( $post_id ) { // Text Field $text_field = $_POST['_text_field_date_expire'][ $post_id ]; if( ! empty( $text_field ) ) { update_post_meta( $post_id, '_text_field_date_expire', esc_attr( $text_field ) ); } } This is to add a new field on each variation of Wocommerce. What I am trying to do is to get each "_text_field_date_expire" meta so I could delete expired variation for each of the products.. How do I get each woocommerce variation ID on functions.php so I could add a rule that it will expire when it is already past the current date right now? var_dump(get_post_meta( $variation_id , '_text_field_date_expire', true )); This code is the one that I will use to get the datetime field, but not sure how I could get each $variation_id field?
Try this one: $args = array( 'post_type' => 'product_variation', 'post_status' => array( 'private', 'publish' ), 'numberposts' => -1, 'orderby' => 'menu_order', 'order' => 'asc', 'post_parent' => get_the_ID() // get parent post-ID ); $variations = get_posts( $args ); foreach ( $variations as $variation ) { // get variation ID $variation_ID = $variation->ID; // get variations meta $product_variation = new WC_Product_Variation( $variation_ID ); // get variation featured image $variation_image = $product_variation->get_image(); // get variation price $variation_price = $product_variation->get_price_html(); get_post_meta( $variation_ID , '_text_field_date_expire', true ); } Hope this will helps you. For more information: How To Get Product Variations & Meta – WooCommerce How to get the Variation ID in a Woocommerce product
In WooCommerce 3+, it's $variation->get_id() from $variation function argument, which is an instance of the WC_Product_Variation. The method get_id() is inherited from WC_Data class. So in your code it should be instead: 'id' => '_text_field_date_expire[' . $variation->get_id() . ']', Since WooCommerce 3, All WC_Product properties can't be accessed directly. Instead, you need to use the available methods. Also in your hooked function save_variation_settings_fields() you are declaring 2 arguments, so there is one missing. It should be: //Save New Fields for Variation// Save Variation Settings add_action( 'woocommerce_save_product_variation', 'save_variation_settings_fields', 10, 2 ); function save_variation_settings_fields( $variation_id, $i ) { // Text Field $text_field = $_POST['_text_field_date_expire'][ $variation_id ]; if( ! empty( $text_field ) ) { update_post_meta( $variation_id, '_text_field_date_expire', esc_attr( $text_field ) ); } } See the source code for woocommerce_save_product_variation action hook
Create programmatically a variable product and two new attributes in WooCommerce
I would like to programmatically create a variable product ("parent" product) with two new variante attributes - all that from a WordPress plugin (so no HTTP Request to an API). These two variante attributes should also be created on the fly. How can this be done ? (with WooCommerce version 3) Update : I have written more lines of code on this that I wished, and tried many things to solve it, using wooCommerce objects, and added missing data about terms, termmeta, the relationship from term with post, in the database using the WordPress database object - but nothing has sufficed to make it work. And I couldn't pin-point where I went wrong - that is why I couldn't provide a narrower problem - things for which stackoverflow is more made for.
After: Create programmatically a WooCommerce product variation with new attribute values Here you get the way to create a new variable product with new product attributes + values: /** * Save a new product attribute from his name (slug). * * #since 3.0.0 * #param string $name | The product attribute name (slug). * #param string $label | The product attribute label (name). */ function save_product_attribute_from_name( $name, $label='', $set=true ){ if( ! function_exists ('get_attribute_id_from_name') ) return; global $wpdb; $label = $label == '' ? ucfirst($name) : $label; $attribute_id = get_attribute_id_from_name( $name ); if( empty($attribute_id) ){ $attribute_id = NULL; } else { $set = false; } $args = array( 'attribute_id' => $attribute_id, 'attribute_name' => $name, 'attribute_label' => $label, 'attribute_type' => 'select', 'attribute_orderby' => 'menu_order', 'attribute_public' => 0, ); if( empty($attribute_id) ) { $wpdb->insert( "{$wpdb->prefix}woocommerce_attribute_taxonomies", $args ); set_transient( 'wc_attribute_taxonomies', false ); } if( $set ){ $attributes = wc_get_attribute_taxonomies(); $args['attribute_id'] = get_attribute_id_from_name( $name ); $attributes[] = (object) $args; //print_r($attributes); set_transient( 'wc_attribute_taxonomies', $attributes ); } else { return; } } /** * Get the product attribute ID from the name. * * #since 3.0.0 * #param string $name | The name (slug). */ function get_attribute_id_from_name( $name ){ global $wpdb; $attribute_id = $wpdb->get_col("SELECT attribute_id FROM {$wpdb->prefix}woocommerce_attribute_taxonomies WHERE attribute_name LIKE '$name'"); return reset($attribute_id); } /** * Create a new variable product (with new attributes if they are). * (Needed functions: * * #since 3.0.0 * #param array $data | The data to insert in the product. */ function create_product_variation( $data ){ if( ! function_exists ('save_product_attribute_from_name') ) return; $postname = sanitize_title( $data['title'] ); $author = empty( $data['author'] ) ? '1' : $data['author']; $post_data = array( 'post_author' => $author, 'post_name' => $postname, 'post_title' => $data['title'], 'post_content' => $data['content'], 'post_excerpt' => $data['excerpt'], 'post_status' => 'publish', 'ping_status' => 'closed', 'post_type' => 'product', 'guid' => home_url( '/product/'.$postname.'/' ), ); // Creating the product (post data) $product_id = wp_insert_post( $post_data ); // Get an instance of the WC_Product_Variable object and save it $product = new WC_Product_Variable( $product_id ); $product->save(); ## ---------------------- Other optional data ---------------------- ## ## (see WC_Product and WC_Product_Variable setters methods) // THE PRICES (No prices yet as we need to create product variations) // IMAGES GALLERY if( ! empty( $data['gallery_ids'] ) && count( $data['gallery_ids'] ) > 0 ) $product->set_gallery_image_ids( $data['gallery_ids'] ); // SKU if( ! empty( $data['sku'] ) ) $product->set_sku( $data['sku'] ); // STOCK (stock will be managed in variations) $product->set_stock_quantity( $data['stock'] ); // Set a minimal stock quantity $product->set_manage_stock(true); $product->set_stock_status(''); // Tax class if( empty( $data['tax_class'] ) ) $product->set_tax_class( $data['tax_class'] ); // WEIGHT if( ! empty($data['weight']) ) $product->set_weight(''); // weight (reseting) else $product->set_weight($data['weight']); $product->validate_props(); // Check validation ## ---------------------- VARIATION ATTRIBUTES ---------------------- ## $product_attributes = array(); foreach( $data['attributes'] as $key => $terms ){ $taxonomy = wc_attribute_taxonomy_name($key); // The taxonomy slug $attr_label = ucfirst($key); // attribute label name $attr_name = ( wc_sanitize_taxonomy_name($key)); // attribute slug // NEW Attributes: Register and save them if( ! taxonomy_exists( $taxonomy ) ) save_product_attribute_from_name( $attr_name, $attr_label ); $product_attributes[$taxonomy] = array ( 'name' => $taxonomy, 'value' => '', 'position' => '', 'is_visible' => 0, 'is_variation' => 1, 'is_taxonomy' => 1 ); foreach( $terms as $value ){ $term_name = ucfirst($value); $term_slug = sanitize_title($value); // Check if the Term name exist and if not we create it. if( ! term_exists( $value, $taxonomy ) ) wp_insert_term( $term_name, $taxonomy, array('slug' => $term_slug ) ); // Create the term // Set attribute values wp_set_post_terms( $product_id, $term_name, $taxonomy, true ); } } update_post_meta( $product_id, '_product_attributes', $product_attributes ); $product->save(); // Save the data } Code goes in function.php file of your active child theme (or active theme). Tested and works. USAGE (example with 2 new attributes + values): create_product_variation( array( 'author' => '', // optional 'title' => 'Woo special one', 'content' => '<p>This is the product content <br>A very nice product, soft and clear…<p>', 'excerpt' => 'The product short description…', 'regular_price' => '16', // product regular price 'sale_price' => '', // product sale price (optional) 'stock' => '10', // Set a minimal stock quantity 'image_id' => '', // optional 'gallery_ids' => array(), // optional 'sku' => '', // optional 'tax_class' => '', // optional 'weight' => '', // optional // For NEW attributes/values use NAMES (not slugs) 'attributes' => array( 'Attribute 1' => array( 'Value 1', 'Value 2' ), 'Attribute 2' => array( 'Value 1', 'Value 2', 'Value 3' ), ), ) ); Tested and works. Related: Create new product attribute programmatically in Woocommerce Create programmatically a WooCommerce product variation with new attribute values Create programmatically a product using CRUD methods in Woocommerce 3
You can also achieve this using the new native functions for setting/getting data from postmeta. Here is an example that works ( based on the default dummy products of Woocommerce 3) //Create main product $product = new WC_Product_Variable(); //Create the attribute object $attribute = new WC_Product_Attribute(); //pa_size tax id $attribute->set_id( 1 ); //pa_size slug $attribute->set_name( 'pa_size' ); //Set terms slugs $attribute->set_options( array( 'blue', 'grey' ) ); $attribute->set_position( 0 ); //If enabled $attribute->set_visible( 1 ); //If we are going to use attribute in order to generate variations $attribute->set_variation( 1 ); $product->set_attributes(array($attribute)); //Save main product to get its id $id = $product->save(); $variation = new WC_Product_Variation(); $variation->set_regular_price(5); $variation->set_parent_id($id); //Set attributes requires a key/value containing // tax and term slug $variation->set_attributes(array( 'pa_size' => 'blue' )); //Save variation, returns variation id $variation->save(); You can add things like weight, tax, sku etc using the native functions available from Woocommerce 3 and on like set_price, set_sku ect. Wrap it within a function and you are good to go.
Sarakinos answer only worked with two small but important modifications. The $attribute->set_id() needs to be set to 0. The $attribute->set_name(); and $variation->set_attributes() do not need the pa_ prefix. The methods already handle the prefixes. So a working code would be: //Create main product $product = new WC_Product_Variable(); //Create the attribute object $attribute = new WC_Product_Attribute(); //pa_size tax id $attribute->set_id( 0 ); // -> SET to 0 //pa_size slug $attribute->set_name( 'size' ); // -> removed 'pa_' prefix //Set terms slugs $attribute->set_options( array( 'blue', 'grey' ) ); $attribute->set_position( 0 ); //If enabled $attribute->set_visible( 1 ); //If we are going to use attribute in order to generate variations $attribute->set_variation( 1 ); $product->set_attributes(array($attribute)); //Save main product to get its id $id = $product->save(); $variation = new WC_Product_Variation(); $variation->set_regular_price(10); $variation->set_parent_id($id); //Set attributes requires a key/value containing // tax and term slug $variation->set_attributes(array( 'size' => 'blue' // -> removed 'pa_' prefix )); //Save variation, returns variation id echo get_permalink ( $variation->save() ); // echo get_permalink( $id ); // -> returns a link to check the newly created product Keep in mind, the product will be bare bones, and will be named 'product'. You need to set those values in the $product before you save it with $id = $product->save();.