Reputation: 5303
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.
Upvotes: 34
Views: 103502
Reputation: 11
In addition to return_false answer: since WC 3.0 in order to solve the issue where the term name is not being selected on variation on the admin you should use set_default_attributes.
$variation->set_weight(''); //LoicTheAztec
$variation->set_default_attributes($variation_data["attributes"]); // Select default attributes
$variation->save(); //LoicTheAztec
Upvotes: 0
Reputation: 11
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();
Upvotes: 1
Reputation: 629
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.
Upvotes: 17
Reputation: 1
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
Upvotes: 0
Reputation: 738
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
Upvotes: 0
Reputation: 627
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')]);
Upvotes: 1
Reputation: 621
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.
Upvotes: 18
Reputation: 254378
Update January 2020: Changed to
WC_Product
methodget_name()
instead ofget_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:
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:
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
Upvotes: 76
Reputation: 23
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 !
Upvotes: 0
Reputation: 41
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 );
}
Upvotes: 2
Reputation: 17276
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.
Upvotes: 5
Reputation: 192
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
),
)
);
}
...
Upvotes: 1
Reputation: 2514
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 );
Upvotes: 8