Create Programmatically a Variable Product and Two New Attributes in Woocommerce

Create programmatically a variable product and two new attributes in WooCommerce

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

Create programmatically a WooCommerce product variation with new attribute values

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:

Sample Image

And it will work perfectly in front end.

Related: Create programmatically a product using CRUD methods in Woocommerce 3

Create multiple product variations issue for a WooCommerce variable product

In your code, the first foreach loop need to be just before the following line of code:

$variation_id = wp_insert_post( $variation_post );

like:

function create_product_variations( $product_id, $variations_data ){
$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()
);

foreach( $variations_data as $variation_data ){

$variation_id = wp_insert_post( $variation_post );

$variation = new WC_Product_Variation( $variation_id );

foreach($_variation_data['attributes'] as $attribute => $term_name){
$taxonomy = 'pa_'.$attribute;

if( ! taxonomy_exists( $taxonomy ) ){
register_taxonomy(
$taxonomy,
'product_variation',
array(
'hierarchical' => false,
'label' => ucfirst($attribute),
'query_var' => true,
'rewrite' => array( 'slug' => sanitize_title($attribute)),
)
);
}

if( ! term_exists( $term_name, $taxonomy ) )
wp_insert_term( $term_name, $taxonomy );

$term_slug = get_term_by('name', $term_name, $taxonomy )->slug;

$post_term_names = wp_get_post_terms( $product_id, $taxonomy, array('fields' => 'names') );

if( ! in_array( $term_name, $post_term_names ) )
wp_set_post_terms( $product_id, $term_name, $taxonomy, true );

update_post_meta( $variation_id, 'attribute_'.$taxonomy, $term_slug );
}
$variation->save();
}
}

As wp_insert_post() create a product variation in database, with a unique variation ID, so it need to be done for each variation in your data array.

This should solve your main related problem.


Related:

  • Create programmatically a WooCommerce product variation with new attribute values
  • Create programmatically a variable product and two new attributes in WooCommerce

Woocommerce create variable product multiple attributes programmatically

there are problems in your code.. the obvious one is that you are updating the post meta inside the foreach loop. Making it overwriting the current value until the last one. Please try the code below.

    if ($product_attributes) {
$thedata = array();
foreach ($product_attributes as $attr) {
$avail_attributes = array();
$avail_attributes = get_terms(wc_attribute_taxonomy_name($attr), $args_t);
$attr = 'pa_'.$attr;
wp_set_object_terms($new_product_id, $avail_attributes, $attr);
$thedata[sanitize_title($attr)] = Array(
'name' => wc_clean($attr),
'value' => '',
'postion' => '0',
'is_visible' => '1',
'is_variation' => '1',
'is_taxonomy' => '1'
);
}
update_post_meta($new_product_id, '_product_attributes', $thedata);
}

Create new product attribute programmatically in Woocommerce

To create a term you can use wp_insert_term()

like so:

wp_insert_term( 'red', 'pa_colors' );

where colors is the name of your attribute. The taxonomy name of an attribute is always prepended by pa_.

Edit Attributes are merely custom taxonomies. Or you could say they are dynamic taxonomies that are manually created by the user in the back-end. Still, all the same, custom taxonomy rules apply.

You can see the source code here which loops through the attributes and runs register_taxonomy() on each. So to create a new attribute (remember it is just a taxonomy) then you need to run register_taxonomy() and simple prepend pa_ to the start of the taxonomy name.

Mimicking some of the values of the taxonomy args from core would get you something like this for a 'Colors' attribute.

/**
* Register a taxonomy.
*/
function so_29549525_register_attribute() {

$permalinks = get_option( 'woocommerce_permalinks' );

$taxonomy_data = array(
'hierarchical' => true,
'update_count_callback' => '_update_post_term_count',
'labels' => array(
'name' => __( 'My Colors', 'your-textdomain' ),
'singular_name' => __( 'Color', 'your-textdomain' ),
'search_items' => __( 'Search colors', 'your-textdomain' ),
'all_items' => __( 'All colors', 'your-textdomain' ),
'parent_item' => __( 'Parent color', 'your-textdomain' ),
'parent_item_colon' => __( 'Parent color:', 'your-textdomain' ),
'edit_item' => __( 'Edit color', 'your-textdomain' ),
'update_item' => __( 'Update color', 'your-textdomain' ),
'add_new_item' => __( 'Add new color', 'your-textdomain' ),
'new_item_name' => __( 'New color', 'your-textdomain' )
),
'show_ui' => false,
'query_var' => true,
'rewrite' => array(
'slug' => empty( $permalinks['attribute_base'] ) ? '' : trailingslashit( $permalinks['attribute_base'] ) . sanitize_title( 'colors' ),
'with_front' => false,
'hierarchical' => true
),
'sort' => false,
'public' => true,
'show_in_nav_menus' => false,
'capabilities' => array(
'manage_terms' => 'manage_product_terms',
'edit_terms' => 'edit_product_terms',
'delete_terms' => 'delete_product_terms',
'assign_terms' => 'assign_product_terms',
)
);

register_taxonomy( 'pa_my_color', array('product'), $taxonomy_data );

}
add_action( 'woocommerce_after_register_taxonomy', 'so_29549525_register_attribute' );

Update 2020-11-18

Attribute taxonomies are stored in the {$wpdb->prefix}woocommerce_attribute_taxonomies database table. And from there WooCommerce runs register_taxonomy() on each one that's found in the table. So in order to create an attribute taxonomy, a row should be added to this table. WooCommerce has a function wc_create_attribute() that will handle this for us. (Since 3.2+).

My conditional logic to test if the attribute exists is not the greatest and I would advise using some kind of version option in your plugin's update routine. But as an example of using wc_create_taxonomy() this should insert an attribute called "My Color".

/**
* Register an attribute taxonomy.
*/
function so_29549525_create_attribute_taxonomies() {

$attributes = wc_get_attribute_taxonomies();

$slugs = wp_list_pluck( $attributes, 'attribute_name' );

if ( ! in_array( 'my_color', $slugs ) ) {

$args = array(
'slug' => 'my_color',
'name' => __( 'My Color', 'your-textdomain' ),
'type' => 'select',
'orderby' => 'menu_order',
'has_archives' => false,
);

$result = wc_create_attribute( $args );

}
}
add_action( 'admin_init', 'so_29549525_create_attribute_taxonomies' );

Woocommerce Programmatically Add Product Attributes and their corresponding values

Okay so this is quite a complicated task, but this is the code that I got working via looking at other's source code and modifying as necessary.

Firstly you will need these helper functions.

  1. To create a global attribute, you need to create a Taxonomy for each one (Gender, Wheel Size, etc), using this function:

    function create_global_attribute($name, $slug)
    {

    $taxonomy_name = wc_attribute_taxonomy_name( $slug );

    if (taxonomy_exists($taxonomy_name))
    {
    return wc_attribute_taxonomy_id_by_name($slug);
    }

    //logg("Creating a new Taxonomy! `".$taxonomy_name."` with name/label `".$name."` and slug `".$slug.'`');

    $attribute_id = wc_create_attribute( array(
    'name' => $name,
    'slug' => $slug,
    'type' => 'select',
    'order_by' => 'menu_order',
    'has_archives' => false,
    ) );

    //Register it as a wordpress taxonomy for just this session. Later on this will be loaded from the woocommerce taxonomy table.
    register_taxonomy(
    $taxonomy_name,
    apply_filters( 'woocommerce_taxonomy_objects_' . $taxonomy_name, array( 'product' ) ),
    apply_filters( 'woocommerce_taxonomy_args_' . $taxonomy_name, array(
    'labels' => array(
    'name' => $name,
    ),
    'hierarchical' => true,
    'show_ui' => false,
    'query_var' => true,
    'rewrite' => false,
    ) )
    );

    //Clear caches
    delete_transient( 'wc_attribute_taxonomies' );

    return $attribute_id;
    }
  2. Now to apply these to your products, I assume you are using Variable Products which each have Variations? The parent Variable Product needs to have their attribute property set to a specially formatted array of the attributes. This function creates that array, creating the global taxonomies for each attribute if they are not found.

    //$rawDataAttributes must be in the form of array("Color"=>array("blue", "red"), "Size"=>array(12,13,14),... etc.)
    function generate_attributes_list_for_product($rawDataAttributes)
    {
    $attributes = array();

    $pos = 0;

    foreach ($rawDataAttributes as $name => $values)
    {
    if (empty($name) || empty($values)) continue;

    if (!is_array($values)) $values = array($values);

    $attribute = new WC_Product_Attribute();
    $attribute->set_id( 0 );
    $attribute->set_position($pos);
    $attribute->set_visible( true );
    $attribute->set_variation( true );

    $pos++;

    //Look for existing attribute:
    $existingTaxes = wc_get_attribute_taxonomies();

    //attribute_labels is in the format: array("slug" => "label / name")
    $attribute_labels = wp_list_pluck( $existingTaxes, 'attribute_label', 'attribute_name' );
    $slug = array_search( $name, $attribute_labels, true );

    if (!$slug)
    {
    //Not found, so create it:
    $slug = wc_sanitize_taxonomy_name($name);
    $attribute_id = create_global_attribute($name, $slug);
    }
    else
    {
    //Otherwise find it's ID
    //Taxonomies are in the format: array("slug" => 12, "slug" => 14)
    $taxonomies = wp_list_pluck($existingTaxes, 'attribute_id', 'attribute_name');

    if (!isset($taxonomies[$slug]))
    {
    //logg("Could not get wc attribute ID for attribute ".$name. " (slug: ".$slug.") which should have existed!");
    continue;
    }

    $attribute_id = (int)$taxonomies[$slug];
    }

    $taxonomy_name = wc_attribute_taxonomy_name($slug);

    $attribute->set_id( $attribute_id );
    $attribute->set_name( $taxonomy_name );
    $attribute->set_options($values);

    $attributes[] = $attribute;
    }

    return $attributes;
    }
  3. We are almost ready to apply the attributes to products, but the main Variable Product and it's Variations need to have the taxonomy terms assigned to them like any Wordpress taxonomy. So if you have a taxonomy called "Wheel Size" which has the terms "28 Inch" and "30 Inch", then the parent product needs to have all "28 Inch" and "30 Inch" terms assigned to it, and the Variations need to have their individual terms assigned to them, so "28 Inch" for one and "30 Inch" for another. To help with this, we use this function to get and/or create these terms if they don't exist:

    function get_attribute_term($value, $taxonomy)
    {
    //Look if there is already a term for this attribute?
    $term = get_term_by('name', $value, $taxonomy);

    if (!$term)
    {
    //No, create new term.
    $term = wp_insert_term($value, $taxonomy);
    if (is_wp_error($term))
    {
    //logg("Unable to create new attribute term for ".$value." in tax ".$taxonomy."! ".$term->get_error_message());
    return array('id'=>false, 'slug'=>false);
    }
    $termId = $term['term_id'];
    $term_slug = get_term($termId, $taxonomy)->slug; // Get the term slug
    }
    else
    {
    //Yes, grab it's id and slug
    $termId = $term->term_id;
    $term_slug = $term->slug;
    }

    return array('id'=>$termId, 'slug'=>$term_slug);
    }

Okay finally now we can use the above to apply Attributes to our products.

For the main Variable Product:

$yourRawAttributeList = array("Gender" => array("Woman", "Man"), "Wheel Size" => array("28 Inch","30 Inch","32 Inch"));
$attribs = generate_attributes_list_for_product($yourRawAttributeList);

$p = new WC_Product_Variable($postID);

$p->set_props(array(
'attributes' => $attribs,
//Set any other properties of the product here you want - price, name, etc.
));

$postID = $p->save();

if ($postID <= 0) return "Unable to create / update product!";

//Attribute Terms: These need to be set otherwise the attributes dont show on the admin backend:
foreach ($attribs as $attrib)
{
/** @var WC_Product_Attribute $attrib */
$tax = $attrib->get_name();
$vals = $attrib->get_options();

$termsToAdd = array();

if (is_array($vals) && count($vals) > 0)
{
foreach ($vals as $val)
{
//Get or create the term if it doesnt exist:
$term = get_attribute_term($val, $tax);

if ($term['id']) $termsToAdd[] = $term['id'];
}
}

if (count($termsToAdd) > 0)
{
wp_set_object_terms($postID, $termsToAdd, $tax, true);
}
}

And for each Variation of that product:

    //This is an array of input attributes in the form: array("Color"=>"Navy", "Size"=>"25")
$theseAttributes = array("Gender" => array("Woman"), "Wheel Size" => array("28 Inch"));

//This is the final list of attributes that we are calculating below.
$theseAttributesCalculated = array();

//logg("Want to add these attributes to the variation: ".print_r($theseAttributes, true));

$existingTax = wc_get_attribute_taxonomies();

foreach ($theseAttributes as $name => $value)
{
if (strlen($name) == 0 || strlen($value) == 0)
{
//logg("Attribute array had a blank value for product variant ".$sku.': '.print_r($theseAttributes, true));
return "Attribute array had a blank value.";
}

$tax = '';
$slug = '';

//Look for an existing taxonomy to match this attribute's $name
//$thistax->attribute_name = slug of the taxonomy
//$thistax->attribute_label = name of the taxonomy

foreach ($existingTax as $thistax)
{
if ($thistax->attribute_label == $name)
{
$slug = $thistax->attribute_name;
$tax = wc_attribute_taxonomy_name($slug);
break;
}
}

if (empty($tax))
{
$slug = wc_sanitize_taxonomy_name($name);
//Taxonomy not found, so create it...
if (create_global_attribute($name, $slug) > 0)
{
$tax = wc_attribute_taxonomy_name($slug);
}
else
{
//logg("Unable to create new attribute taxonomy ".$slug." for attribute ".$name."found in variable product ".$sku);
continue;
}
}

//logg("Want to add attribute ".$name. " value ".$value. " which is term ".$term_slug." (".$termId.") to post ".$parentID);

$term = get_attribute_term($value, $tax);

if ($term['id'])
{
// Set/save the attribute data in the product variation
$theseAttributesCalculated[$tax] = $term['slug'];
}
else
{
//logg("Warning! Unable to create / get the attribute ".$value." in Taxonomy ".$tax);
}


Related Topics



Leave a reply



Submit