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 codes 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.
Add variable products programmatically To create variable products programmatically in WooCommerce, you just need to change the second parameter of the wp_set_object_terms() function: wp_set_object_terms( $post_id, 'variable', 'product_type' ); Similarly, you can define a product as grouped or external.
Get WooCommerce attributes programmatically For attributes, usually the best way is using the $product->get_attribute( $name ) method. This returns all values you have for that WooCommerce attribute in that particular product.
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:
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.
$attribute->set_id()
needs to be set to 0
.$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();
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With