Puddinq.com sharing knowledge

Programmatticaly create a WooCommerce product with variations

Programmatticaly create a WooCommerce product with variations

Sometimes you need to alter a lot of products, or create extra options. Even if bulk edit could do it and you max to editing 200 pages at once, it is still a tedious job vulnerable for human error. You need a script. The snippets below will not fix your job but give you the insight in what is possible. At the end only your imagination is the limit.

With automated scripts I usually start with a shortcode and when I see it running correct I convert it to a single php file requiring wp-load.php at the top, so I have all WordPress functions available. But for now:

A place where we will build our code

Make a new page and add a shortcode: [create-products]

In your functions.php (or maybe a better / other place) add:

add_shortcode('create-products', 'test_create_products');

function test_create_products() {

    // START we wil only be working between the start an end tags here
    return 'test';
    // END


We need products to start with

First we need a product we can work with. Later you will probably loop through all your products, filtering them and adjusting properties (in batches) to edit your whole shop. A basis loop could look like this:

add_shortcode('create-products', 'test_create_products');

function test_create_products() {

    // START
    $productPosts = get_posts([
        'post_type'         => 'product',
        'numberposts'       => 20,
        'suppress_filters'  => true

    $output = '';
    /* @var $product WP_Post */
    foreach ($productPosts as $productPost) {
        $output .= $productPost->post_title . '<br>';
    return $output;
    // END


(one script I used a similar loop to update existing products with attributes: link)

If you adjust your shortcode function with the exact content above, the page will show you the titles of max 20 of your products. $productPost is a WP_Post object here. To be able to handle it as a product we would need to create a product object based on the id:

$product = wc_get_product($productPost->ID)

Creating one product

In this walkthrough and your testing it is best to avoid working with your live database, and focus on one product. Once that is working you can use all techniques to batch alter all your products. If that is working you can move the script to your live development.

We will create a new product called ‘First product’ in a way you can rerun the script (refresh your page with the shortcode) until your satisfied:

    // START
    $product = get_page_by_title( 'First product', OBJECT, 'product' );

    // check if the product already exists
    if (is_null($product)) {

        $post = array(
            'post_content' => '',
            'post_status' => "publish",
            'post_title' => 'First product' ,
            'post_parent' => '',
            'post_type' => "product"

        $product_id = wp_insert_post( $post, false );

        $product = wc_get_product( $product_id );

    } else {

        $product = wc_get_product($product->ID);


    $output = 'Product title: ' . $product->get_title() . '<br>';
    $output = 'Product id: ' . $product->get_id() . '<br>';
    $output .= 'Product type: ' . $product->get_type() . '<br>';
    $output .= 'Product price: ' . $product->get_price() . '<br>';

    return $output;
    // END

The above creates a product, with a title automatically generated id and the type simple.
The price is empty because we did not set that jet. But as we are calling the properties title, id and price as easily as possible from the WooCommerce product object, we can change the just as easy (just have to save them):

    // add these two lines just before..

    // these lines
    $output = 'Product title: ' . $product->get_title() . '<br>';
    $output = 'Product id: ' . $product->get_id() . '<br>';
    $output .= 'Product type: ' . $product->get_type() . '<br>';
    $output .= 'Product price: ' . $product->get_price() . '<br>';

If you refresh the page now, the product is not created (it already was there), but the price output will be filled with the amount you have set. Combine this knowledge with the loop in the beginning, and you can adjust all you prices in a few seconds.

Double your price?

    $product->set_regular_price($product->get_price() * 2);

Variations, what you are here for

All we have to do to transform the simple product to a variable product is tot add to the script:

    // this makes it a variable product
    wp_set_object_terms($product->get_id(), 'variable', 'product_type');

Now the product is a variable. Variations as actually child posts of this variable product. So we will make some variations to this product, set prices and stock and make their parent post the variable product.

Creating an attribute

You can have two sorts of attributes, custom ones or some that are linked to a taxonomy. To create that taxonomy we will create the following separate function to register the ‘My attribute’ taxonomy: (load any wp-admin page to register it)

function my_create_attribute_taxonomy() {

    $attributes = wc_get_attribute_taxonomies();

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

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

        $args = array(
            'slug'    => 'my_attribute',
            'name'   => 'My attribute',
            'type'    => 'select',
            'orderby' => 'menu_order',
            'has_archives'  => false,

        $result = wc_create_attribute( $args );

add_action( 'admin_init', 'my_create_attribute_taxonomy' );

Now we have a custom taxonomy, next we will add a term to it:

Add a term (attribute) to the custom taxonomy

add_shortcode('create-products', 'test_create_products');

function test_create_products() {

    // START

        $newAttribute = wp_insert_term( 'My first attribute', 'pa_my_attribute', array(
            'description' => 'this is my first programmatically generated attribute',
            'slug' => 'my_first_attribute'
        ) );

        echo '<pre>';
        echo '</pre>';
    // END


If you refresh your shortcode page you will see a term_id and in wp-admin you will see the attribute is created in your custom attribute taxonomy. The taxonomy for these attributes always have a default pa_ prefix before the slug you thought you gave it. This is a WooCommerce prefix for product (p) attribute (a) taxonomies.

We have a product, attribute (taxonomy) and an attribute. Let’s combine them


add_shortcode('create-products', 'test_create_products');

function test_create_products() {

    // START

    // we have our product, just get the post
    $productPost = get_page_by_title( 'First product', OBJECT, 'product' );

    // get the product object linked to the post
    $product = wc_get_product($productPost->ID);

    // This wil get the id for the 'my_first_attribute attribute' in the taxonomy 'pa_my_attribute'
    $term_taxonomy_ids = wp_set_object_terms( $product->get_id(), 'my_first_attribute', 'pa_my_attribute', false );

    // get the full term object
    $term = get_term_by('id', $term_taxonomy_ids[0], 'pa_my_attribute', OBJECT);

    // Now we will add it as a attribute connected to the product
    $theData = Array(
            'name'=> 'pa_my_attribute',
            'value'=> $term->slug,
            'is_visible' => '1',
            'is_variation' => '1',
            'is_taxonomy' => '1'
    update_post_meta( $product->get_id(),'_product_attributes', $theData);

    // And finally a variation is created
    $variation_post = array(
        'post_title'  => $product->get_title() . ' - ' . $term->name,
        'post_name'   => sanitize_title($product->get_title() . ' ' . $term->name),
        'post_status' => 'publish',
        'post_excerpt'=> 'My attribute: ' . $term->name,
        'post_parent' => $product->get_id(),
        'post_type'   => 'product_variation',
        'guid'        => $product->get_permalink(),
        'meta_input'  => array(
            'attribute_pa_my_attribute' => $term->slug

    // Creating the product variation
    $variation_id = wp_insert_post( $variation_post );

    $variation = new WC_Product_Variation( $variation_id );

    ## Set/save all other data

    // SKU
    // Prices
    $variation->set_price( 10 );
    $variation->set_regular_price( 10 );


    // Stock
    $variation->set_weight(''); // weight (resetting)
    $variation->save(); // Save the data

    // END