16

My store sells vinyl stickers. Each product (sticker) has a 144 variations (24 colors, 3 sizes and 2 orientation). Each variation is necessary to assign a unique SKU.

Manually populate catalog is unrealistic. I am going to make a form in which the user specifies the name, description, and the main image of the product, as well as the possible sizes and colors. When processing the form i need to create a product and all of its variations.

How to create a product and its variations?

j0k
  • 22,600
  • 28
  • 79
  • 90
Beer Brother
  • 514
  • 2
  • 6
  • 15
  • Do you need to create an image or just print out possible options? – Peon Jul 16 '12 at 11:50
  • 4
    Now there is a V2 REST API, I would recommend using that to create products. The API can log in as a different user, and it caters for all the subtleties that may be overlooked when constructing a product manually. It also involves just putting a single large data structure together and sending it off to be set up in one action. – Jason Feb 28 '15 at 23:41
  • 1
    WooCommerce has introduced new CRUD objects in 3.0 - it is strongly recommended that you start using this, instead of using pure Wordpress functions to update reverse-engineered data. https://github.com/woocommerce/woocommerce/wiki/CRUD-Objects-in-3.0 – Pavel Lishin Apr 29 '19 at 18:09

7 Answers7

17

I had a similar situation, here's what I found out.

Products are actually a custom post type (quite obvious! :P), so you can use wp_insert_post to insert a new product. After you insert, you get the id of the new product post type, use update_post_meta to set a meta key and meta value as _visibility and visible respectively. If you don't set the visibility, your newly added product will never be visible in your shop. Alternatively you can set the visibility from the back end as well. For the various sizes of the product, use variations on that product. You can set a different type, price, SKU etc. for each variation. All these are post meta, hence you can use php code to add the variations and stuff. Study the postmeta table to see the key names.

jasonlfunk
  • 5,159
  • 4
  • 29
  • 39
Avant Garde
  • 220
  • 2
  • 9
  • when I set `update_post_meta( $new_product_post_id, '_visibility', 'hidden' );` but on product detail backend page. It still show **visible** value. what wrong ? – huykon225 Jun 12 '17 at 02:36
17

As Jason wrote in his comment, REST API is the way to go here. This can be done even without HTTP REST requests, making it work even on Wordpress installations that have their REST interface disabled.

Here is a simple function that I made for this purpose:

$products_controler = new WC_REST_Products_Controller();
function create_item($rest_request) {
    global $products_controler;
    if (!isset($rest_request['status']))
        $rest_request['status'] = 'publish';
    $wp_rest_request = new WP_REST_Request('POST');
    $wp_rest_request->set_body_params($rest_request);
    return $products_controler->create_item($wp_rest_request);
}

Here, $rest_request is an array that you'd usually send via REST (see the docs here).

The $products_controler variable is global because I needed to call this function multiple times and I didn't want to recreate the object each time. Feel free to make it local.

This works for all types of products (simple, grouped, variable,...) and it should be more resistant to internal changes of WooCommerce than adding the products manually through wp_insert_post and update_post_meta.

Edit: Given that this answer still get an occasional upvote, here is a WooCommerce 3.0+ update. The change is that the variations no longer get added automatically, so we have to do it by ourselves.

This is the current version of the function:

protected function create_item( $rest_request ) {
    if ( ! isset( $rest_request['status'] ) ) {
        $rest_request['status'] = $this->plugin->get_option( 'default_published_status' );
    }
    if ( ! isset( $this->products_controler ) ) {
        $this->products_controler = new WC_REST_Products_Controller();
    }
    $wp_rest_request = new WP_REST_Request( 'POST' );
    $wp_rest_request->set_body_params( $rest_request );
    $res = $this->products_controler->create_item( $wp_rest_request );
    $res = $res->data;
    // The created product must have variations
    // If it doesn't, it's the new WC3+ API which forces us to build those manually
    if ( ! isset( $res['variations'] ) )
        $res['variations'] = array();
    if ( count( $res['variations'] ) == 0 && count( $rest_request['variations'] ) > 0 ) {
        if ( ! isset( $this->variations_controler ) ) {
            $this->variations_controler = new WC_REST_Product_Variations_Controller();
        }
        foreach ( $rest_request['variations'] as $variation ) {
            $wp_rest_request = new WP_REST_Request( 'POST' );
            $variation_rest = array(
                'product_id' => $res['id'],
                'regular_price' => $variation['regular_price'],
                'image' => array( 'id' => $variation['image'][0]['id'], ),
                'attributes' => $variation['attributes'],
            );
            $wp_rest_request->set_body_params( $variation_rest );
            $new_variation = $this->variations_controler->create_item( $wp_rest_request );
            $res['variations'][] = $new_variation->data;
        }
    }
    return $res;
}

This is used in Kite Print and Dropshipping on Demand plugin, starting from the (soon to be published) version 1.1, in file api/publish_products.php.

There is also a much longer "fast" version called create_products_fast which writes directly to the database, making it potentially less resilient to future WP/WC changes, but it is much faster (few seconds vs. few minutes for our 34 products range on my test computer).

Community
  • 1
  • 1
Vedran Šego
  • 3,553
  • 3
  • 27
  • 40
  • Hello, the version `create_products_fast` is part of the WC or used on the Kite Print plugin? – Jeans K. Real Nov 23 '17 at 13:48
  • 1
    @JeansK.Real It is a function in the Kite's (now discontinued) plugin, in which all the SQL requests normally done through many WP functions (a few hundred calls in our case) were grouped in only 5 or 6 bulk SQL calls, making it blazingly fast, but also not very resilient to WP/WC changes. As a backup for such cases, we had a setting that would switch the feature on or off (in the latter case, the above function was used instead). – Vedran Šego Nov 24 '17 at 12:36
11

This is the most logical and easy way. Simple Product use the below Code:

$objProduct = new WC_Product();

and for variable product use below line of code.

$objProduct = new WC_Product_Variable();

Next step is to set the meta Properties like name, price etc and variants data (in case of variable product).

$objProduct->set_name("Product Title");
$objProduct->set_status("publish");  // can be publish,draft or any wordpress post status
$objProduct->set_catalog_visibility('visible'); // add the product visibility status
$objProduct->set_description("Product Description");
$objProduct->set_sku("product-sku"); //can be blank in case you don't have sku, but You can't add duplicate sku's
$objProduct->set_price(10.55); // set product price
$objProduct->set_regular_price(10.55); // set product regular price
$objProduct->set_manage_stock(true); // true or false
$objProduct->set_stock_quantity(10);
$objProduct->set_stock_status('instock'); // in stock or out of stock value
$objProduct->set_backorders('no');
$objProduct->set_reviews_allowed(true);
$objProduct->set_sold_individually(false);
$objProduct->set_category_ids(array(1,2,3)); // array of category ids, You can get category id from WooCommerce Product Category Section of Wordpress Admin

Below code is used if you want to upload images of product

function uploadMedia($image_url){
    require_once('wp-admin/includes/image.php');
    require_once('wp-admin/includes/file.php');
    require_once('wp-admin/includes/media.php');
    $media = media_sideload_image($image_url,0);
    $attachments = get_posts(array(
        'post_type' => 'attachment',
        'post_status' => null,
        'post_parent' => 0,
        'orderby' => 'post_date',
        'order' => 'DESC'
    ));
    return $attachments[0]->ID;
}
// above function uploadMedia, I have written which takes an image url as an argument and upload image to wordpress and returns the media id, later we will use this id to assign the image to product.
$productImagesIDs = array(); // define an array to store the media ids.
$images = array("image1 url","image2 url","image3 url"); // images url array of product
foreach($images as $image){
    $mediaID = uploadMedia($image); // calling the uploadMedia function and passing image url to get the uploaded media id
    if($mediaID) $productImagesIDs[] = $mediaID; // storing media ids in a array.
}
if($productImagesIDs){
    $objProduct->set_image_id($productImagesIDs[0]); // set the first image as primary image of the product

        //in case we have more than 1 image, then add them to product gallery. 
    if(count($productImagesIDs) > 1){
        $objProduct->set_gallery_image_ids($productImagesIDs);
    }
}

save the product to get WooCommerce Product ID

$product_id = $objProduct->save(); // it will save the product and return the generated product id

Note : For a Simple Product the above Steps are Enough, the below Steps are for Variable Product or Product having Attributes.

Below Code is Used to add Product Attributes.

$attributes = array(
    array("name"=>"Size","options"=>array("S","L","XL","XXL"),"position"=>1,"visible"=>1,"variation"=>1),
    array("name"=>"Color","options"=>array("Red","Blue","Black","White"),"position"=>2,"visible"=>1,"variation"=>1)
);
if($attributes){
    $productAttributes=array();
    foreach($attributes as $attribute){
        $attr = wc_sanitize_taxonomy_name(stripslashes($attribute["name"])); // remove any unwanted chars and return the valid string for taxonomy name
        $attr = 'pa_'.$attr; // woocommerce prepend pa_ to each attribute name
        if($attribute["options"]){
            foreach($attribute["options"] as $option){
                wp_set_object_terms($product_id,$option,$attr,true); // save the possible option value for the attribute which will be used for variation later
            }
        }
        $productAttributes[sanitize_title($attr)] = array(
            'name' => sanitize_title($attr),
            'value' => $attribute["options"],
            'position' => $attribute["position"],
            'is_visible' => $attribute["visible"],
            'is_variation' => $attribute["variation"],
            'is_taxonomy' => '1'
        );
    }
    update_post_meta($product_id,'_product_attributes',$productAttributes); // save the meta entry for product attributes
}

Below Code is Used to add Product Variants.

$variations = array(
    array("regular_price"=>10.11,"price"=>10.11,"sku"=>"ABC1","attributes"=>array(array("name"=>"Size","option"=>"L"),array("name"=>"Color","option"=>"Red")),"manage_stock"=>1,"stock_quantity"=>10),
    array("regular_price"=>10.11,"price"=>10.11,"sku"=>"ABC2","attributes"=>array(array("name"=>"Size","option"=>"XL"),array("name"=>"Color","option"=>"Red")),"manage_stock"=>1,"stock_quantity"=>10)
    
);
if($variations){
    try{
        foreach($variations as $variation){
            $objVariation = new WC_Product_Variation();
            $objVariation->set_price($variation["price"]);
            $objVariation->set_regular_price($variation["regular_price"]);
            $objVariation->set_parent_id($product_id);
            if(isset($variation["sku"]) && $variation["sku"]){
                $objVariation->set_sku($variation["sku"]);
            }
            $objVariation->set_manage_stock($variation["manage_stock"]);
            $objVariation->set_stock_quantity($variation["stock_quantity"]);
            $objVariation->set_stock_status('instock'); // in stock or out of stock value
            $var_attributes = array();
            foreach($variation["attributes"] as $vattribute){
                $taxonomy = "pa_".wc_sanitize_taxonomy_name(stripslashes($vattribute["name"])); // name of variant attribute should be same as the name used for creating product attributes
                $attr_val_slug =  wc_sanitize_taxonomy_name(stripslashes($vattribute["option"]));
                $var_attributes[$taxonomy]=$attr_val_slug;
            }
            $objVariation->set_attributes($var_attributes);
            $objVariation->save();
        }
    }
    catch(Exception $e){
        // handle exception here
    }
}

That's It The above Code is enough to add a Simple or Variable WooCommerce Product with Images, Attributes and Variants.

10

Based on Vedran's answer, here's the minimal code for posting a WooCommerce product via PHP:

$data = [
    'name' => 'Test product',
    'description' => 'Lorem ipsum',
];
$request = new WP_REST_Request( 'POST' );
$request->set_body_params( $data );
$products_controller = new WC_REST_Products_Controller;
$response = $products_controller->create_item( $request );
Sophivorus
  • 3,008
  • 3
  • 33
  • 43
  • 1
    Thank you! This way is absolutely right as it using WooCommerce API which chnages each time. I tested this code and its creates all nessesary products data in meta and other tables. – realmag777 Dec 12 '17 at 16:37
2

Please refer to woocommerce-with-php-code, where the full code is given.

As Avant Garde said, products are added in post with:

post_type = "product"

Obsidian Age
  • 41,205
  • 10
  • 48
  • 71
herr
  • 837
  • 1
  • 8
  • 21
0

i use this code:

$sku = 21333;
$size = 'S';
$stock = 2;
$price_a = 60;
$price_b = 30;

$product_parent = get_product_by_sku($sku);
$product = new WC_Product_Variable($product_parent->id);
$variations = $product->get_available_variations();

// First off all delete all variations
foreach($variations as $prod_variation) {
  $metaid=mysql_query("SELECT meta_id FROM wp_postmeta WHERE post_id = ".$prod_variation['variation_id']);
  while ($row = mysql_fetch_assoc($metaid)) {
    mysql_query("DELETE FROM wp_postmeta WHERE meta_id = ".$row['meta_id']);
  }
  mysql_query("DELETE FROM wp_posts WHERE ID = ".$prod_variation['variation_id']);
}

// Now add new variation
$thevariation = array(
  'post_title'=> '',
  'post_name' => 'product-' . $product_parent->id . '-variation',
  'post_status' => 'publish',
  'post_parent' => $product_parent->id,
  'post_type' => 'product_variation',
  'guid'=>home_url() . '/?product_variation=product-' . $product_parent->id . '-variation'
);
$variation_id = wp_insert_post( $thevariation );
update_post_meta($variation_id, 'post_title', 'Variation #' . $variation_id . ' of '. $product_parent->id);

wp_set_object_terms( $variation_id, $size, 'pa_size' );
update_post_meta($variation_id, 'attribute_pa_size', $size);

update_post_meta($variation_id, '_regular_price', $price_a);
update_post_meta($variation_id, '_downloadable_files', '');
update_post_meta($variation_id, '_download_expiry', '');
update_post_meta($variation_id, '_download_limit', '');
update_post_meta($variation_id, '_sale_price_dates_to', '');
update_post_meta($variation_id, '_sale_price_dates_from', '');
update_post_meta($variation_id, '_backorders', 'no');
update_post_meta($variation_id, '_stock_status', 'instock');
update_post_meta($variation_id, '_height', '');
update_post_meta($variation_id, '_manage_stock', 'yes');
update_post_meta($variation_id, '_width', '');
update_post_meta($variation_id, '_sale_price_dates_from', '');
update_post_meta($variation_id, '_backorders', 'no');
update_post_meta($variation_id, '_stock_status', 'instock');
update_post_meta($variation_id, '_manage_stock', 'yes');
update_post_meta($variation_id, '_height', '');
update_post_meta($variation_id, '_width', '');
update_post_meta($variation_id, '_length', '');
update_post_meta($variation_id, '_weight', '');
update_post_meta($variation_id, '_downloadable', 'no');
update_post_meta($variation_id, '_virtual', 'no');
update_post_meta($variation_id, '_thumbnail_id', '0');
update_post_meta($variation_id, '_sku', '');

update_post_meta($variation_id, '_sale_price', $price_b);
update_post_meta($variation_id, '_price', $price_b);
update_post_meta($product_parent->id, '_min_variation_price', $price_b);
update_post_meta($product_parent->id, '_max_variation_price', $price_b);
update_post_meta($product_parent->id, '_min_price_variation_id', $variation_id);
update_post_meta($product_parent->id, '_max_price_variation_id', $variation_id);
update_post_meta($product_parent->id, '_min_variation_regular_price', $price_a);
update_post_meta($product_parent->id, '_max_variation_regular_price', $price_a);
update_post_meta($product_parent->id, '_min_regular_price_variation_id', $variation_id);
update_post_meta($product_parent->id, '_max_regular_price_variation_id', $variation_id);
update_post_meta($product_parent->id, '_min_variation_sale_price', $price_b);
update_post_meta($product_parent->id, '_max_variation_sale_price', $price_b);
update_post_meta($product_parent->id, '_min_sale_price_variation_id', $variation_id);
update_post_meta($product_parent->id, '_max_sale_price_variation_id', $variation_id);
update_post_meta($product_parent->id, '_price', $price_b);

update_post_meta( $variation_id, '_stock', $stock );
update_post_meta($product_parent->id, 'post_status', 'publish');
vijay
  • 1
0

if you already set up your woocommerce then you can use this code

<?php
        global $paged;
        $curpage = $paged ? $paged : 1;
        $args = array(
            'post_type'           => 'product',
            'posts_per_page'      => -1,
            'paged' => $paged,
        );
        $loop = new WP_Query($args);
            if ($loop->have_posts()){
                    while ($loop->have_posts()) : $loop->the_post();
                    $product = wc_get_product(get_the_ID());
                    $get_id = get_the_ID();
                    $product_details = $product->get_data();
                    $Plink=get_permalink($loop->post->ID);
                    ?>

this code below with a tag use this in you add cart button

<a href="?add-to-cart=<?php echo $get_id;?>" data-quantity="1" class="button wp-element-button product_type_simple add_to_cart_button ajax_add_to_cart added" data-product_id="<?php echo $get_id;?>" data-product_sku="" aria-label="Add “<?php the_title(); ?>” to your cart" rel="nofollow">buy now</a>