Related
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();
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();
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();
I'm using this slightly modified script (inside a loop) which helps me generate two links which I use to add products to the cart:
<?php
// Get the ID of the product
$id = $_POST["post_var"];
// Get variations
$args = array(
'post_type' => 'product_variation',
'post_status' => array( 'publish' ),
'numberposts' => 2,
'orderby' => 'menu_order',
'order' => 'asc',
'post_parent' => $product->id
);
$variations = get_posts( $args );
$loop = 0;
if ( $variations ) {
foreach ( $variations as $variation ) {
$formats[$loop]["id"] = absint( $variation->ID );
$formats[$loop]["data"] = get_post_meta( $variation->ID );
$variation_label = "?????????";
$loop++;
}
foreach ( $formats as $type )
$response .= '' . $variation_label . '';
} ?>
<?php echo $response; ?>
The only thing I can't figure out is how to get the variation labels.
Every product has variation called "Format' with the options "Electronic" and "Physical" and I need those to be output into the link. So the $response would look something like this:
<a href="http://example.com/checkout?add-to-cart=93&variation_id=94&quantity=1&attribute_pa_format=Electronic">Electronic</a
Physical
You can see I've added a variable called $variation_label as a placeholder.
Stumped.
I am trying to update a specific field for all variations in a product, but unfortunately, I am unable to get variation ids to go further.
Basically what I am doing is, I am trying to get each variation's stock value and if the stock value is less than zero, then I am updating a particular field with certain values (as mentioned in my code below).
I have tested this by manually inputting any custom post / variation id and is working fine, updates that particular variation id based on its stock value. All I am stuck here is I am unable to fetch the variation id by itself.
Below is the code that I am using:
global $post, $woocommerce;
$post_id = $variation->ID;
// Get specific data from the certain custom fields using get_post_meta( $post_id, $key, $single );
$stock = get_post_meta( $post_id, '_stock', true );
if ($stock < 1) {
update_post_meta( $post_id, 'cuzd-prod-general-v', '20,25' );
} else {
update_post_meta( $post_id, 'cuzd-prod-general-v', '1,5' );
}
I don't know where I am wrong to get the variation ids for this.
I was able to solve this by using below code changes:
// Values to be updated
$in_stock = '1,5';
$out_of_stock = '20,30';
// Get variations
$args = array(
'post_type' => 'product_variation',
'post_status' => array( 'private', 'publish' ),
'numberposts' => -1,
'orderby' => 'menu_order',
'order' => 'asc',
'post_parent' => $post->ID
);
$variations = get_posts( $args );
foreach ( $variations as $variation ) {
$variation_id = absint( $variation->ID );$variable_id = $this['variation_id'];
$variation_post_status = esc_attr( $variation->post_status );
$variation_data = get_post_meta( $variation_id );
$variation_data['variation_post_id'] = $variation_id;
// Get specific data from the certain custom fields using get_post_meta( $post_id, $key, $single );
$stock = get_post_meta( $variation_data['variation_post_id'], '_stock', true );
if ($stock < 1) {
update_post_meta( $variation_data['variation_post_id'], 'cuzd-prod-general-v', $out_of_stock );
} else {
update_post_meta( $variation_data['variation_post_id'], 'cuzd-prod-general-v', $in_stock );
}
}