Create programmatically a product using CRUD methods in Woocommerce 3 - php
I'd like to create a new Woocommerce product programmatically with PHP code and I have tried it with the Woocommerce 3 CRUD methods:
//Woocommerce CRUD
$objProduct = new WC_Product_Simple();
$objProduct->set_name('My Product 1'); //Set product name.
$objProduct->set_status('publish'); //Set product status.
//$objProduct->set_featured(FALSE); //Set if the product is featured. | bool
$objProduct->set_catalog_visibility('visible'); //Set catalog visibility. | string $visibility Options: 'hidden', 'visible', 'search' and 'catalog'.
$objProduct->set_description('My custom long description'); //Set product description.
//$objProduct->set_short_description('My short description'); //Set product short description.
//$objProduct->set_sku('U-123'); //Set SKU
$objProduct->set_price(5.00); //Set the product's active price.
//$objProduct->set_regular_price(5.00); //Set the product's regular price.
//$objProduct->set_sale_price(); //Set the product's sale price.
//$objProduct->set_date_on_sale_from(); //Set date on sale from. | string|integer|null $date UTC timestamp, or ISO 8601 DateTime. If the DateTime string has no timezone or offset, WordPress site timezone will be assumed. Null if their is no date.
//$objProduct->set_date_on_sale_to();//Set date on sale to. | string|integer|null $date UTC timestamp, or ISO 8601 DateTime. If the DateTime string has no timezone or offset, WordPress site timezone will be assumed. Null if their is no date.
//$objProduct->set_manage_stock(TRUE); //Set if product manage stock. | bool
//$objProduct->set_stock_quantity(10); //Set number of items available for sale.
//$objProduct->set_stock_status('instock'); //Set stock status. | string $status 'instock', 'outofstock' and 'onbackorder'
//$objProduct->set_backorders('no'); //Set backorders. | string $backorders Options: 'yes', 'no' or 'notify'.
//$objProduct->set_sold_individually(FALSE); //Set if should be sold individually. | bool
//$objProduct->set_weight(); //Set the product's weight.
//$objProduct->set_length(); //Set the product length.
//$objProduct->set_width(); //Set the product width.
//$objProduct->set_height(); //Set the product height.
//$objProduct->set_upsell_ids($upsell_ids); //Set upsell IDs. | array $upsell_ids IDs from the up-sell products.
//$objProduct->set_cross_sell_ids($cross_sell_ids); //Set crosssell IDs. | array $cross_sell_ids IDs from the cross-sell products.
$objProduct->set_reviews_allowed(TRUE); //Set if reviews is allowed. | bool
//$objProduct->set_purchase_note($purchase_note); //Set purchase note. | string $purchase_note Purchase note.
$attribute = new WC_Product_Attribute();
$attribute->set_id(wc_attribute_taxonomy_id_by_name('pa_color')); //if passing the attribute name to get the ID
$attribute->set_name('pa_color'); //attribute name
$attribute->set_options('red'); // attribute value
$attribute->set_position(1); //attribute display order
$attribute->set_visible(1); //attribute visiblity
$attribute->set_variation(0);//to use this attribute as varint or not
$raw_attributes[] = $attribute; //<--- storing the attribute in an array
$attribute = new WC_Product_Attribute();
$attribute->set_id(25);
$attribute->set_name('pa_size');
$attribute->set_options('XL');
$attribute->set_position(2);
$attribute->set_visible(1);
$attribute->set_variation(0);
$raw_attributes[] = $attribute; //<--- storing the attribute in an array
$objProduct->set_attributes($raw_attributes); //Set product attributes. | array $raw_attributes Array of WC_Product_Attribute objects.
//$objProduct->set_category_ids($term_ids); //Set the product categories. | array $term_ids List of terms IDs.
//$objProduct->set_tag_ids($term_ids); //Set the product tags. | array $term_ids List of terms IDs.
//$objProduct->set_image_id(); //Set main image ID. | int|string $image_id Product image id.
//$objProduct->set_gallery_image_ids(); //Set gallery attachment ids. | array $image_ids List of image ids.
$new_product_id = $objProduct->save(); //Saving the data to create new product, it will return product ID.
return;
}
But I get this error:
22-Oct-2018 20:25:19 UTC] PHP Fatal error: Uncaught Error: Class 'WC_Product_Simple' not found in /home/u1771p590/domains/famyfuerza.com/public_html/store/wp-content/plugins/ff-dropship-data-scraper/ff-dropship-data-scraper.php:165
Stack trace:
- #0 /home/u1771p590/domains/famyfuerza.com/public_html/store/wp-content/plugins/ff-dropship-data-scraper/ff-dropship-data-scraper.php(233): curl_download('https://www.ban...')
- #1 /home/u1771p590/domains/famyfuerza.com/public_html/store/wp-settings.php(305): include_once('/home/u1771p590...')
- #2 /home/u1771p590/domains/famyfuerza.com/public_html/store/wp-config.php(112): require_once('/home/u1771p590...')
- #3 /home/u1771p590/domains/famyfuerza.com/public_html/store/wp-load.php(37): require_once('/home/u1771p590...')
- #4 /home/u1771p590/domains/famyfuerza.com/public_html/store/wp-admin/admin.php(31): require_once('/home/u1771p590...')
- #5 /home/u1771p590/domains/famyfuerza.com/public_html/store/wp-admin/index.php(10): require_once('/home/u1771p590...')
- #6 {main}
thrown in /home/u1771p590/domains/famyfuerza.com/public_html/store/wp-content/plugins/ff-dropship-data-scraper/ff-dropship-data-scraper.php on line 165
I have tried to include the simple product class, but that produced another error which said that another class was missing.
Does somebody know how to fix this? Or has another method to create a woocommerce product via code?
You are not accessing the WC_Product_simple instance object from your custom Dropship Data Scraper plugin.
The guilty can be mostly 2 things:
You have not installed Woocommerce.
The plugin Dropship Data Scraper is outdated and needs changes, to handle woocommerce.
Try to include the global Woocommerce object and to enable Woocommerce support in your plugin.
To create a product programmatically using CRUD methods introduced in Woocommerce 3 here is the correct way to make it work.
If you need to handle product attributes creation or product variations creation, refer to:
Create new product attribute programmatically in Woocommerce
Create programmatically a WooCommerce product variation with new attribute values
Create programmatically a variable product and two new attributes in WooCommerce
All product attributes and their term values need to be created before. Same thing for product categories and product tags.
1) The code functions:
// Custom function for product creation (For Woocommerce 3+ only)
function create_product( $args ){
if( ! function_exists('wc_get_product_object_type') && ! function_exists('wc_prepare_product_attributes') )
return false;
// Get an empty instance of the product object (defining it's type)
$product = wc_get_product_object_type( $args['type'] );
if( ! $product )
return false;
// Product name (Title) and slug
$product->set_name( $args['name'] ); // Name (title).
if( isset( $args['slug'] ) )
$product->set_name( $args['slug'] );
// Description and short description:
$product->set_description( $args['description'] );
$product->set_short_description( $args['short_description'] );
// Status ('publish', 'pending', 'draft' or 'trash')
$product->set_status( isset($args['status']) ? $args['status'] : 'publish' );
// Visibility ('hidden', 'visible', 'search' or 'catalog')
$product->set_catalog_visibility( isset($args['visibility']) ? $args['visibility'] : 'visible' );
// Featured (boolean)
$product->set_featured( isset($args['featured']) ? $args['featured'] : false );
// Virtual (boolean)
$product->set_virtual( isset($args['virtual']) ? $args['virtual'] : false );
// Prices
$product->set_regular_price( $args['regular_price'] );
$product->set_sale_price( isset( $args['sale_price'] ) ? $args['sale_price'] : '' );
$product->set_price( isset( $args['sale_price'] ) ? $args['sale_price'] : $args['regular_price'] );
if( isset( $args['sale_price'] ) ){
$product->set_date_on_sale_from( isset( $args['sale_from'] ) ? $args['sale_from'] : '' );
$product->set_date_on_sale_to( isset( $args['sale_to'] ) ? $args['sale_to'] : '' );
}
// Downloadable (boolean)
$product->set_downloadable( isset($args['downloadable']) ? $args['downloadable'] : false );
if( isset($args['downloadable']) && $args['downloadable'] ) {
$product->set_downloads( isset($args['downloads']) ? $args['downloads'] : array() );
$product->set_download_limit( isset($args['download_limit']) ? $args['download_limit'] : '-1' );
$product->set_download_expiry( isset($args['download_expiry']) ? $args['download_expiry'] : '-1' );
}
// Taxes
if ( get_option( 'woocommerce_calc_taxes' ) === 'yes' ) {
$product->set_tax_status( isset($args['tax_status']) ? $args['tax_status'] : 'taxable' );
$product->set_tax_class( isset($args['tax_class']) ? $args['tax_class'] : '' );
}
// SKU and Stock (Not a virtual product)
if( isset($args['virtual']) && ! $args['virtual'] ) {
$product->set_sku( isset( $args['sku'] ) ? $args['sku'] : '' );
$product->set_manage_stock( isset( $args['manage_stock'] ) ? $args['manage_stock'] : false );
$product->set_stock_status( isset( $args['stock_status'] ) ? $args['stock_status'] : 'instock' );
if( isset( $args['manage_stock'] ) && $args['manage_stock'] ) {
$product->set_stock_status( $args['stock_qty'] );
$product->set_backorders( isset( $args['backorders'] ) ? $args['backorders'] : 'no' ); // 'yes', 'no' or 'notify'
}
}
// Sold Individually
$product->set_sold_individually( isset( $args['sold_individually'] ) ? $args['sold_individually'] : false );
// Weight, dimensions and shipping class
$product->set_weight( isset( $args['weight'] ) ? $args['weight'] : '' );
$product->set_length( isset( $args['length'] ) ? $args['length'] : '' );
$product->set_width( isset( $args['width'] ) ? $args['width'] : '' );
$product->set_height( isset( $args['height'] ) ? $args['height'] : '' );
if( isset( $args['shipping_class_id'] ) )
$product->set_shipping_class_id( $args['shipping_class_id'] );
// Upsell and Cross sell (IDs)
$product->set_upsell_ids( isset( $args['upsells'] ) ? $args['upsells'] : '' );
$product->set_cross_sell_ids( isset( $args['cross_sells'] ) ? $args['upsells'] : '' );
// Attributes et default attributes
if( isset( $args['attributes'] ) )
$product->set_attributes( wc_prepare_product_attributes($args['attributes']) );
if( isset( $args['default_attributes'] ) )
$product->set_default_attributes( $args['default_attributes'] ); // Needs a special formatting
// Reviews, purchase note and menu order
$product->set_reviews_allowed( isset( $args['reviews'] ) ? $args['reviews'] : false );
$product->set_purchase_note( isset( $args['note'] ) ? $args['note'] : '' );
if( isset( $args['menu_order'] ) )
$product->set_menu_order( $args['menu_order'] );
// Product categories and Tags
if( isset( $args['category_ids'] ) )
$product->set_category_ids( $args['category_ids'] );
if( isset( $args['tag_ids'] ) )
$product->set_tag_ids( $args['tag_ids'] );
// Images and Gallery
$product->set_image_id( isset( $args['image_id'] ) ? $args['image_id'] : "" );
$product->set_gallery_image_ids( isset( $args['gallery_ids'] ) ? $args['gallery_ids'] : array() );
## --- SAVE PRODUCT --- ##
$product_id = $product->save();
return $product_id;
}
// Utility function that returns the correct product object instance
function wc_get_product_object_type( $type = 'simple' ) {
// Get an instance of the WC_Product object (depending on his type)
if( $type === 'variable' ){
$product = new WC_Product_Variable();
} elseif( $type === 'grouped' ){
$product = new WC_Product_Grouped();
} elseif( $type === 'external' ){
$product = new WC_Product_External();
} else {
$product = new WC_Product_Simple(); // "simple" By default
}
if( ! is_a( $product, 'WC_Product' ) )
return false;
else
return $product;
}
// Utility function that prepare product attributes before saving
function wc_prepare_product_attributes( $attributes ){
global $woocommerce;
$data = array();
$position = 0;
foreach( $attributes as $taxonomy => $values ){
if( ! taxonomy_exists( $taxonomy ) )
continue;
// Get an instance of the WC_Product_Attribute Object
$attribute = new WC_Product_Attribute();
$term_ids = array();
// Loop through the term names
foreach( $values['term_names'] as $term_name ){
if( term_exists( $term_name, $taxonomy ) )
// Get and set the term ID in the array from the term name
$term_ids[] = get_term_by( 'name', $term_name, $taxonomy )->term_id;
else
continue;
}
$taxonomy_id = wc_attribute_taxonomy_id_by_name( $taxonomy ); // Get taxonomy ID
$attribute->set_id( $taxonomy_id );
$attribute->set_name( $taxonomy );
$attribute->set_options( $term_ids );
$attribute->set_position( $position );
$attribute->set_visible( $values['is_visible'] );
$attribute->set_variation( $values['for_variation'] );
$data[$taxonomy] = $attribute; // Set in an array
$position++; // Increase position
}
return $data;
}
2) USAGE: - Example for a simple product creation with 2 product attributes "Color" and "Size":
$product_id = create_product( array(
'type' => '', // Simple product by default
'name' => __("The product title", "woocommerce"),
'description' => __("The product description…", "woocommerce"),
'short_description' => __("The product short description…", "woocommerce"),
// 'sku' => '',
'regular_price' => '5.00', // product price
// 'sale_price' => '',
'reviews_allowed' => true,
'attributes' => array(
// Taxonomy and term name values
'pa_color' => array(
'term_names' => array('Red', 'Blue'),
'is_visible' => true,
'for_variation' => false,
),
'pa_size' => array(
'term_names' => array('X Large'),
'is_visible' => true,
'for_variation' => false,
),
),
) );
// Displaying the created product ID
echo $product_id;
Tested and works using only Woocommerce 3 CRUD methods.
Another way: https://wordpress.stackexchange.com/a/137578/74284 , Use wp_insert_post function.
https://lukasznowicki.info/insert-new-woocommerce-product-programmatically/
$item = array(
'Name' => 'Product A',
'Description' => 'This is a product A',
'SKU' => '10020030A',
);
$user_id = get_current_user(); // this has NO SENSE AT ALL, because wp_insert_post uses current user as default value
// $user_id = $some_user_id_we_need_to_use; // So, user is selected..
$post_id = wp_insert_post( array(
'post_author' => $user_id,
'post_title' => $item['Name'],
'post_content' => $item['Description'],
'post_status' => 'publish',
'post_type' => "product",
) );
wp_set_object_terms( $post_id, 'simple', 'product_type' );
update_post_meta( $post_id, '_visibility', 'visible' );
update_post_meta( $post_id, '_stock_status', 'instock');
update_post_meta( $post_id, 'total_sales', '0' );
update_post_meta( $post_id, '_downloadable', 'no' );
update_post_meta( $post_id, '_virtual', 'yes' );
update_post_meta( $post_id, '_regular_price', '' );
update_post_meta( $post_id, '_sale_price', '' );
update_post_meta( $post_id, '_purchase_note', '' );
update_post_meta( $post_id, '_featured', 'no' );
update_post_meta( $post_id, '_weight', '' );
update_post_meta( $post_id, '_length', '' );
update_post_meta( $post_id, '_width', '' );
update_post_meta( $post_id, '_height', '' );
update_post_meta( $post_id, '_sku', $item['SKU'] );
update_post_meta( $post_id, '_product_attributes', array() );
update_post_meta( $post_id, '_sale_price_dates_from', '' );
update_post_meta( $post_id, '_sale_price_dates_to', '' );
update_post_meta( $post_id, '_price', '' );
update_post_meta( $post_id, '_sold_individually', '' );
update_post_meta( $post_id, '_manage_stock', 'no' );
update_post_meta( $post_id, '_backorders', 'no' );
update_post_meta( $post_id, '_stock', '' );
Related
How to create custom sort in Woocommerce
I am creating a custom sort name "price-asc" which sort products based on highest price. My solution is: 1- create key_meta called "_highest_price" to store the highest price for each product 2- create a custom "price-asc" in filter woocommerce_catalog_orderby 3- custom "orderby", "order", "meta_key" in filter woocommerce_get_catalog_ordering_args functions.php // step 1 function my_save_post_product( $post_id, $post, $update ) { if( ! $update ) // no further actions if no changes return; if( $post->post_status !== 'publish' ) // no further actions if product is not public return; $product = wc_get_product( $post_id ); if( $product->product_type == 'variable' ) : update_post_meta( $post_id, '_highest_price', $product->get_variation_price( 'max', true ) ); elseif( $product->product_type == 'simple' ) : update_post_meta( $post_id, '_highest_price', $product->get_price() ); endif; } add_action( 'save_post_product', 'my_save_post_product', 10, 3 ); // step 2 function my_woocommerce_catalog_orderby( $options ) { //... $options['price-asc'] = __( 'A different low to high', 'mytheme' ); return $options; } add_filter( 'woocommerce_catalog_orderby', 'my_woocommerce_catalog_orderby' ); // step 3 function my_woocommerce_get_catalog_ordering_args( $args ) { $orderby_value = isset( $_GET['orderby'] ) ? woocommerce_clean( $_GET['orderby'] ) : apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) ); if ( $orderby_value == 'price-asc' ) : $args['orderby'] = 'meta_value_num'; $args['order'] = 'ASC'; $args['meta_key'] = '_highest_price'; endif; return $args; } add_filter( 'woocommerce_get_catalog_ordering_args', 'my_woocommerce_get_catalog_ordering_args' ); Then I go to https://example.com/products/?orderby=price-asc However, the order doesn't display as expected. Product with _highest_price "22092" should displays before product with _highest_price "1177000" as 22092 < 1177000. Here are my database and display. Please help database display
After a while, I finally got my answer. I just simply changed "price-asc" to "price_asc". Why? In woocommerce\includes\class-wc-query.php before line 461: $orderby_value = "price-asc" line 461: $orderby_value = is_array( $orderby_value ) ? $orderby_value : explode( '-', $orderby_value ); after this line $orderby_value is an array with [0] => "price", [1] => "asc" line 462: $orderby = esc_attr( $orderby_value[0] ); this time, $orderby = "price" which tells the system continuously runs at lines 498-501 case 'price': $callback = 'DESC' === $order ? 'order_by_price_desc_post_clauses' : 'order_by_price_asc_post_clauses'; add_filter( 'posts_clauses', array( $this, $callback ) ); break; => it's wrong. $orderby should not be "price" at the end. My suggestion: it might only apply for keyword "price" but for the future, we should name our sorting id with "_" instead of "-". E.g custom_sort_id instead of custom-sort-id.
Show Custom Field Product Information on WooCommerce Checkout Page
I'm trying to display multiple custom product fields on the checkout page. I've found the below code which works for one custom field but how can I add multiple custom fields to it? add_filter( 'woocommerce_get_item_data', 'display_custom_product_field_data', 10, 2 ); function display_custom_product_field_data( $cart_data, $cart_item ) { // Define HERE your product custom field meta key <== <== <== <== <== $meta_key = 'custom_time'; $product_id = $cart_item['product_id']; $meta_value = get_post_meta( $product_id, $meta_key, true ); if( !empty( $cart_data ) ) $custom_items = $cart_data; if( !empty($meta_value) ) { $custom_items[] = array( 'key' => __('Time', 'woocommerce'), 'value' => $meta_value, 'display' => $meta_value, ); } return $custom_items; }
you can define $meta_keys as array $meta_keys = array('custom_time','custom_time2'); // or more than tow and other Field $dictionary = array('custom_time'=>'Time' , 'custom_time2'=>'Date') $product_id = $cart_item['product_id']; foreach($meta_keys as $key=>$meta_key){ $meta_value = get_post_meta( $product_id, $meta_key, true ); if( !empty( $cart_data ) ) $custom_items = $cart_data; if( !empty($meta_value) ) { $custom_items[] = array( 'key' => __( $dictionary[$meta_key] , 'woocommerce'), //or user $meta_key 'value' => $meta_value, 'display' => $meta_value, ); } } return $custom_items;
Add fee on cart items based on custom field for each item and add it on cart and checkout page
I am using woocommerce and I am using a custom post type (products) for my products This is the code that integrates my custom post type to woocommerce class WCCPT_Product_Data_Store_CPT extends WC_Product_Data_Store_CPT { /** * Method to read a product from the database. * #param WC_Product */ public function read( &$product ) { $product->set_defaults(); if ( ! $product->get_id() || ! ( $post_object = get_post( $product->get_id() ) ) || ! in_array( $post_object->post_type, array( 'products', 'product' ) ) ) { throw new Exception( __( 'Invalid product.', 'woocommerce' ) ); } $id = $product->get_id(); $product->set_props( array( 'name' => $post_object->post_title, 'slug' => $post_object->post_name, 'date_created' => 0 < $post_object->post_date_gmt ? wc_string_to_timestamp( $post_object->post_date_gmt ) : null, 'date_modified' => 0 < $post_object->post_modified_gmt ? wc_string_to_timestamp( $post_object->post_modified_gmt ) : null, 'status' => $post_object->post_status, 'description' => $post_object->post_content, 'short_description' => $post_object->post_excerpt, 'parent_id' => $post_object->post_parent, 'menu_order' => $post_object->menu_order, 'product_id' => $product->is_type( 'variation' ) ? $product->get_parent_id() : $product->get_id(), 'reviews_allowed' => 'open' === $post_object->comment_status, 'price' => $post_object->price ) ); $this->read_attributes( $product ); $this->read_downloads( $product ); $this->read_visibility( $product ); $this->read_product_data( $product ); $this->read_extra_data( $product ); $product->set_object_read( true ); } public function get_product_type( $product_id ) { $post_type = get_post_type( $product_id ); if ( 'product_variation' === $post_type ) { return 'variation'; } elseif ( in_array( $post_type, array( 'products', 'product' ) ) ) { // change birds with your post type $terms = get_the_terms( $product_id, 'product_type' ); return ! empty( $terms ) ? sanitize_title( current( $terms )->name ) : 'simple'; } else { return false; } } } add_filter( 'woocommerce_data_stores', 'woocommerce_data_stores' ); function woocommerce_data_stores ( $stores ) { $stores['product'] = 'WCCPT_Product_Data_Store_CPT'; return $stores; } so I know can add to cart the custom post type by adding price on it I added a custom field using woocommerce_form_field() which is called "Track Length" and will be multiplied per $24 ( 2 x $24 = $28 ) Now I tried using "woocommerce_before_calculate_totals" filter and its not working, the set_price() is not working maybe because its a custom post type so now I am getting the computation inside "woocommerce_product_get_price" filter but its not working per item in cart Here is the code inside my functions.php https://codeshare.io/5eN797
Saving custom field data from Admin WooCommerce product data metabox
In Woocommerce I use some custom fields for product specifications, and save the specifications in the post_meta. I'm trying to make an if loop to write down in the post_meta another product specification. The code I now use is: add_action( 'woocommerce_product_options_general_product_data', 'BTW_field' ); function BTW_field() { woocommerce_wp_radio( array( 'id' => '_BTW', 'default' => '21% BTW', 'required' => true, 'options' => array( 'Prijs is incl. 21% BTW' => '21% BTW', 'Margeproduct' => 'Marge Product', ) ) ); } add_action( 'woocommerce_process_product_meta', 'BTW_save' ); function BTW_save( $post_id ){ $BTW = $_POST['_BTW']; if( !empty( $BTW ) ) update_post_meta( $post_id, '_BTW', esc_attr( $BTW ) ); } An now I try to rewrite the BTW_save function so it will save another post_meta. function BTW_save( $post_id ){ $BTW = $_POST['_BTW']; if( !empty( $BTW ) ){ update_post_meta( $post_id, '_BTW', esc_attr( $BTW ) ); } if ($BTW == "Margeproduct (vrijgesteld van BTW)"){ $BTW2 = "Margeproduct*" } else { $BTW2 = "21%" } update_post_meta( $post_id, '_BTW_NAME', esc_attr( $BTW2 ) ); } I don't know how I can check if $BTW is equal to the post_meta _BTW and how I can rewrite it so $BTW2 will also save in the post meta as _BTW_NAME.
Updated: As you are setting 2 different values, it could be better to use a select field instead. Also I have make some changes in your code regarding correct variable naming and field keys naming (You should be able to rename them easily keeping in mind that lowercase and underscores are recommended). Here is the code: add_action( 'woocommerce_product_options_general_product_data', 'add_btw_field' ); function add_btw_field() { global $post; // Get the selected value $value = get_post_meta( $post->ID, '_btw', true ); if( empty( $value ) ) $value = 'btw'; // Default value woocommerce_wp_select( array( 'id' => 'btw_select', 'label' => __( 'BTW-prijsopties', 'woocommerce' ), 'options' => array( 'btw' => __( '21% BTW', 'woocommerce' ), 'marge' => __( 'Marge Product', 'woocommerce' ), ), 'value' => $value, // Displaying the selected value ) ); } add_action( 'woocommerce_process_product_meta', 'save_btw_field' ); function save_btw_field( $post_id ){ if( empty( $_POST['btw_select'] ) ) return; // exit (in case of) update_post_meta( $post_id, '_btw', esc_attr( $_POST['btw_select'] ) ); if ( $_POST['btw_select'] == 'btw' ) $label = __( 'BTW 21%', 'woocommerce' ); else $label = __( 'Margeproduct (vrijgesteld van BTW)', 'woocommerce' ); update_post_meta( $post_id, '_btw_label', esc_attr( $label ) ); } Code goes in function.php file of your active child theme (or theme) or also in any plugin file. Tested and works. You will get something like that: By default when creating or updating a product, both custom fields will be saved for 'btw' option as product meta data… You will be able to get both product post_meta custom fields values using get_post_meta(): // Set HERE the product ID (or get it dynamically) $product_id = 37; $btw = get_post_meta( $product_id, '_btw', true ); // Values can be 'yes' or 'no' $btw_label = get_post_meta( $product_id, '_btw_label', true ); // Output (testing): echo $btw_label;
How to set attribute as variation in Woocommerce
I have a front-end form I am creating to allow users to publish a variable product to my shop from the front end with predefined attributes and variations. I have found this very helpful question: here Which shows me how to set the product type as variable and assign my predefined attributes in the attributes section of the product data. However when I am on the backend of Wordpress/Woocommerce and editing the product I click on variations and none are set, I look at the attributes and my "resolution" attribute is set with my 3 items. How do I make this to where it will actually set those attributes to variations of my form? Do I need to use wp_insert_post? Looking in phpmyadmin it just looks like product variations are assigned to a parent_id (product id) and post type is product_varition and so on. $new_post = array( 'post_title' => esc_attr(strip_tags($_POST['postTitle'])), 'post_content' => esc_attr(strip_tags($_POST['postContent'])), 'post_status' => 'publish', 'post_type' => 'product', 'tags_input' => array($tags) ); $skuu = rand(); $post_id = wp_insert_post($new_post); update_post_meta($post_id, '_sku', $skuu ); //my array for setting the attributes $avail_attributes = array( 'high-resolution', 'medium-resolution', 'low-resolution' ); //Sets the attributes up to be used as variations but doesnt actually set them up as variations wp_set_object_terms ($post_id, 'variable', 'product_type'); wp_set_object_terms( $post_id, $avail_attributes, 'pa_resolution' ); $thedata = array( 'pa_resolution'=> array( 'name'=>'pa_resolution', 'value'=>'', 'is_visible' => '1', 'is_variation' => '1', 'is_taxonomy' => '1' ) ); update_post_meta( $post_id,'_product_attributes',$thedata); update_post_meta( $post_id, '_visibility', 'search' ); update_post_meta( $post_id, '_stock_status', 'instock'); So just to be clear (I tend to be confusing) the above does create my variable product from the front end, and when I look at the product in the backend it is a variable product, it has the resolution attribute set and has my 3 terms (high-res, medium-res, low-res) as attributes. I just need to take this a step further where they are actually set as variations so people can place an order.
I got it working for my situation by using update_post_meta and wp_insert_post. Because I already setup my attributes and terms all I needed was a way to add to the above code so that when the product is created it not only will assign the attributes to the product but insert them as variations in the database. Here is my solution: //insert variations post_type $i=1; while ($i<=3) { $my_post = array( 'post_title' => 'Variation #' . $i . ' of ' . esc_attr(strip_tags($_POST['postTitle'])), 'post_name' => 'product-' . $post_id . '-variation-' . $i, 'post_status' => 'publish', 'post_parent' => $post_id, 'post_type' => 'product_variation', 'guid' => home_url() . '/?product_variation=product-' . $post_id . '-variation-' . $i ); // Insert the post into the database wp_insert_post( $my_post ); $variable_id = $post_id + 1; $variable_two = $variable_id + 1; $variable_three = $variable_two + 1; update_post_meta( $variable_id, 'attribute_pa_resolution', 'high-resolution'); update_post_meta( $variable_id, '_price', 8.50 ); update_post_meta( $variable_id, '_regular_price', '8.50'); update_post_meta( $variable_two, 'attribute_pa_resolution', 'medium-resolution'); update_post_meta( $variable_two, '_price', 5.50 ); update_post_meta( $variable_two, '_regular_price', '5.50'); update_post_meta( $variable_three, 'attribute_pa_resolution', 'low-resolution'); update_post_meta( $variable_three, '_price', 3.50 ); update_post_meta( $variable_three, '_regular_price', '3.50'); $i++; }
I have been struggling for the last two days on implementing in my plugin the ability to set up product variation in Woocommerce. I managed to get all correct data in DB but Woocommerce wasn't retrieving as I thought it would. The problem were the 'transient' stored in wp_options table. This is the code that had me up and running with WC 2.6.0: CREATING VARIATION BASED UPON SELECTED ATTRIBUTES ($_POST['term_id_arr'] are a list of all attributes chosen from frontend for creating product. I'm sorry but I am not yet allowed to post images on stack.. Front-end form with variations created) $opzioni_arr=$_POST['term_id_arr']; $qta = count($opzioni_arr); if ($qta>0){ $ID = $post_id; //make product type be variable: wp_set_object_terms ($ID,'variable','product_type'); foreach ($opzioni_arr as $opzioni_val){ $new_arr = explode("/",$opzioni_val); $att_taxonomy = $new_arr[0]; $att_slug = $new_arr[1]; $att_id =$new_arr[2]; $att_arr[$att_taxonomy][$att_id]=$att_slug; $att_due_arr[$att_taxonomy][$att_taxonomy.'*'.$att_id]=$att_slug; } //################### Add attributes to main product: #################### foreach ($att_arr as $att_arr_key=>$att_arr_val){ //Array for setting attributes unset ($avail_attributes); foreach ($att_arr_val as $key_two=>$val_two){ $avail_attributes[] = $val_two; } wp_set_object_terms($ID, $avail_attributes, $att_arr_key); $thedata [$att_arr_key]= Array( 'name'=>$att_arr_key, 'value'=>'', 'is_visible' => 1, 'is_variation' => 1, 'is_taxonomy' => 1, 'position' => '1' ); } update_post_meta( $ID,'_product_attributes',$thedata); //########################## Done adding attributes to product ################# //function to create combinations from attributes $combinations=lasap_get_combinations($att_due_arr); foreach ($combinations as $key=>$val){ // Start creating variations // The variation is simply a post // with the main product set as its parent // you might want to put that in a loop or something // to create multiple variations with multiple values $parent_id = $ID; $variation = array( 'post_title' => 'Prodotto #' . $parent_id . ' Variante', 'post_content' => '', 'post_status' => 'publish', 'post_parent' => $parent_id, 'post_type' => 'product_variation' ); // The variation id $variation_id = wp_insert_post( $variation ); // Regular Price ( you can set other data like sku and sale price here ) update_post_meta($variation_id, '_sku', $sku); update_post_meta($variation_id, '_regular_price', $regular_price); update_post_meta($variation_id, '_sale_price', $sale_price); update_post_meta($variation_id, '_sale_price_dates_from', $datasaldodal_time); update_post_meta($variation_id, '_sale_price_dates_to', $datasaldoal_time); if (($sale_price!='' AND ($sale_price<=$regular_price))){$price=$sale_price;} else {$price=$regular_price;} update_post_meta($variation_id, '_price', $price); update_post_meta($variation_id, '_thumbnail_id', $thumbnail_id); update_post_meta($variation_id, '_manage_stock', $manage_stock); update_post_meta($variation_id, '_stock_status', $stock_status); update_post_meta($variation_id, '_stock', $stock); update_post_meta($variation_id, '_stock_soglia', $stock_soglia); update_post_meta($variation_id, '_backorders', $backorders); foreach ($val as $chiave=>$valore){ $exp = explode ("*", $chiave); $attributo_nome = $exp[0]; update_post_meta( $variation_id, 'attribute_' . $attributo_nome, $valore ); $split_attributo = explode("_", $attributo_nome); $attributo_nome_due = $split_attributo[1]; echo "<p>Variante #$variation_id: $attributo_nome_due ($valore)</p>"; } } do_action( 'product_variation_linked', $variation_id ); // Update parent if variable so price sorting works and stays in sync with the cheapest child lasap_woo_sync_varianti ($post_id, $stock_status_before); } delete_transient( 'wc_product_children_' . $parent_id ); UPDATING VARIATIONS (SUCH AS PRICE, SALE PRICE AND SO ON...). $sku_arr = $_POST['sku']; $prezzolistino_arr = $_POST['prezzolistino']; //regular_price $prezzosaldo_arr = $_POST['prezzosaldo']; //sale_price $datasaldoal_arr = $_POST['datasaldoal']; //sale_date_to $datasaldodal_arr = $_POST['datasaldodal']; //sale_date_from $manage_stock_arr = $_POST['manage_stock']; $stock_arr = $_POST['stock']; $stock_soglia_arr = $_POST['stock_soglia']; $backorders_arr = $_POST['backorders']; foreach ($sku_arr as $varID=>$sku){ $prezzolistino = $prezzolistino_arr[$varID]; $prezzosaldo = $prezzosaldo_arr[$varID]; if (($prezzosaldo!='' AND ($prezzosaldo<=$prezzolistino))){$prezzoesposto=$prezzosaldo;} else {$prezzoesposto=$prezzolistino;} $prezzoesposto = $prezzoesposto * 1; $datasaldoal = $datasaldoal_arr[$varID]; $datasaldodal = $datasaldodal_arr[$varID]; $manage_stock = $manage_stock_arr[$varID]; $stock = $stock_arr[$varID]; $stock_soglia = $stock_soglia_arr[$varID]; $backorders = $backorders_arr[$varID]; switch ($manage_stock){ case "on": $manage_stock = "yes"; break; default : $manage_stock = "no"; } switch ($backorders){ case "on": $backorders = "yes"; break; default : $backorders = "no"; } update_post_meta ($varID, '_sku', $sku); update_post_meta ($varID, '_regular_price', $prezzolistino); update_post_meta ($varID, '_sale_price', $prezzosaldo); update_post_meta ($varID, '_price', $prezzoesposto); update_post_meta ($varID, '_sale_price_dates_to', $datasaldoal); update_post_meta ($varID, '_sale_price_dates_from', $datasaldodal); update_post_meta ($varID, '_manage_stock', $manage_stock); update_post_meta ($varID, '_stock', $stock); update_post_meta ($varID, '_stock_soglia', $stock_soglia); update_post_meta ($varID, '_backorders', $backorders); } // Update parent if variable so price sorting works and stays in sync with the cheapest child $tipo = $_POST['tipo'];//this is a value to check if I am updating main product or its children if ($tipo != 'parent'){ lasap_woo_sync_varianti ($post_id, $stock_status_before); } DELETING VARIATION(S) $ID_arr = $_POST['ID']; foreach ($ID_arr as $ID){ $res = wp_delete_post($ID, true); } $variazioni = lasap_woo_check_variazioni($post_id); if ($variazioni > 0){ // Update parent if variable so price sorting works and stays in sync with the cheapest child lasap_woo_sync_varianti ($post_id, $stock_status_before); } else { //clean up main product delete_post_meta($post_id, '_product_attributes'); delete_post_meta($post_id, '_min_variation_price'); delete_post_meta($post_id, '_max_variation_price'); delete_post_meta($post_id, '_min_price_variation_id'); delete_post_meta($post_id, '_max_price_variation_id'); delete_post_meta($post_id, '_min_variation_regular_price'); delete_post_meta($post_id, '_max_variation_regular_price'); delete_post_meta($post_id, '_min_regular_price_variation_id'); delete_post_meta($post_id, '_max_regular_price_variation_id'); delete_post_meta($post_id, '_min_variation_sale_price'); delete_post_meta($post_id, '_max_variation_sale_price'); delete_post_meta($post_id, '_min_sale_price_variation_id'); delete_post_meta($post_id, '_max_sale_price_variation_id'); update_post_meta($post_id, '_stock_status', 'instock'); wp_set_object_terms ($post_id,'simple','product_type'); wc_delete_product_transients( $post_id ); } FUNCTION CALLED FOR SYNCING.... function lasap_woo_sync_varianti ($post_id, $stock_status_before){ //https://docs.woocommerce.com/wc-apidocs/class-WC_Product_Variable.html //WC_Product_Variable::variable_product_sync( $post_id );//sync variable product prices with the children lowest/highest (calls :sync) //WC_Product_Variable::sync_stock_status( $post_id );//sync the variable product stock status with children //WC_Product_Variable::sync_attributes( $post_id );//sync the variable product's attributes with the variations (called by :sync) WC_Product_Variable::sync( $post_id );//sync the variable product with it's children wc_delete_product_transients( $post_id ); lasap_get_variation_prices ($post_id); $stock_status_after = get_post_meta($post_id, '_stock_status', true); update_post_meta($post_id, '_stock_status', $stock_status_before); } FUNCTION FOR SETTING TRANSIENT (EXTRACTED FROM woocommerce\includes\class-wc-product-variable.php) function lasap_get_variation_prices( $post_id, $display = false ) { global $wp_filter, $woocommerce, $wpdb; $_product = wc_get_product( $post_id ); /** * Transient name for storing prices for this product (note: Max transient length is 45) * #since 2.5.0 a single transient is used per product for all prices, rather than many transients per product. */ $transient_name = 'wc_var_prices_' . $post_id; /** * Create unique cache key based on the tax location (affects displayed/cached prices), product version and active price filters. * DEVELOPERS should filter this hash if offering conditonal pricing to keep it unique. * #var string */ if ( $display ) { $price_hash = array( get_option( 'woocommerce_tax_display_shop', 'excl' ), WC_Tax::get_rates() ); } else { $price_hash = array( false ); } $filter_names = array( 'woocommerce_variation_prices_price', 'woocommerce_variation_prices_regular_price', 'woocommerce_variation_prices_sale_price' ); foreach ( $filter_names as $filter_name ) { if ( ! empty( $wp_filter[ $filter_name ] ) ) { $price_hash[ $filter_name ] = array(); foreach ( $wp_filter[ $filter_name ] as $priority => $callbacks ) { $price_hash[ $filter_name ][] = array_values( wp_list_pluck( $callbacks, 'function' ) ); } } } $price_hash = md5( json_encode( apply_filters( 'woocommerce_get_variation_prices_hash', $price_hash, $_product, $display ) ) ); // If the value has already been generated, we don't need to grab the values again. if ( empty( $post_id->prices_array[ $price_hash ] ) ) { // Get value of transient $prices_array = array_filter( (array) json_decode( strval( get_transient( $transient_name ) ), true ) ); // If the product version has changed, reset cache if ( empty( $prices_array['version'] ) || $prices_array['version'] !== WC_Cache_Helper::get_transient_version( 'product' ) ) { $post_id->prices_array = array( 'version' => WC_Cache_Helper::get_transient_version( 'product' ) ); } // If the prices are not stored for this hash, generate them //if ( empty( $prices_array[ $price_hash ] ) ) { if ( 1>0 ) { $prices = array(); $regular_prices = array(); $sale_prices = array(); $variation_ids = $wpdb->get_col("SELECT ID FROM $wpdb->posts WHERE post_type='product_variation' AND post_status='publish' AND post_parent=$post_id", 0); foreach ( $variation_ids as $variation_id ) { $post_meta = get_post_meta($variation_id); $price = $post_meta['_price'][0]; $regular_price = $post_meta['_regular_price'][0]; $sale_price = $post_meta['_sale_price'][0]; // Skip empty prices if ( '' === $price ) { continue; } // If sale price does not equal price, the product is not yet on sale if ( $sale_price === $regular_price || $sale_price !== $price ) { $sale_price = $regular_price; } // If we are getting prices for display, we need to account for taxes if ( $display ) { if ( 'incl' === get_option( 'woocommerce_tax_display_shop' ) ) { $price = '' === $price ? '' : $variation->get_price_including_tax( 1, $price ); $regular_price = '' === $regular_price ? '' : $variation->get_price_including_tax( 1, $regular_price ); $sale_price = '' === $sale_price ? '' : $variation->get_price_including_tax( 1, $sale_price ); } else { $price = '' === $price ? '' : $variation->get_price_excluding_tax( 1, $price ); $regular_price = '' === $regular_price ? '' : $variation->get_price_excluding_tax( 1, $regular_price ); $sale_price = '' === $sale_price ? '' : $variation->get_price_excluding_tax( 1, $sale_price ); } } $prices[ $variation_id ] = wc_format_decimal( $price, wc_get_price_decimals() ); $regular_prices[ $variation_id ] = wc_format_decimal( $regular_price, wc_get_price_decimals() ); $sale_prices[ $variation_id ] = wc_format_decimal( $sale_price . '.00', wc_get_price_decimals() ); } asort( $prices ); asort( $regular_prices ); asort( $sale_prices ); $prices_array[ $price_hash ] = array( 'price' => $prices, 'regular_price' => $regular_prices, 'sale_price' => $sale_prices, ); echo "<br> 659) ";mostra_array($prices_array); set_transient( $transient_name, json_encode( $prices_array ), DAY_IN_SECONDS * 30 ); } /** * Give plugins one last chance to filter the variation prices array which has been generated. */ $post_id->prices_array[ $price_hash ] = apply_filters( 'woocommerce_variation_prices', $prices_array[ $price_hash ], $post_id, $display ); } } FUNCTION I USE TO CHECK IF PRODUCT HAS VARIATIONS (RETURN QTY) function lasap_woo_check_variazioni($post_id){ global $wpdb; $qry_var=" SELECT COUNT(ID) FROM $wpdb->posts WHERE post_type='product_variation' AND post_parent=$post_id AND post_status='publish'"; $res_var=$wpdb->get_var($qry_var); return $res_var; } After all this, another problem rose up (it NEVER happens, doesn'it?): I haven't figured it out why but after updating variations, parent product post_meta '_stock_status' was always set as 'outofstock'. I have patched this retrieving at the beginning the correct status with get_post_meta and passing its value to the function lasap_woo_sync_varianti. Any improvements are greatly appreciated and hope this will help someone to save his/her time! Tested and working on WC 2.6.13. See link here
update_post_meta( $post_id, '_visibility', PARAM' ) It will have 3 or 4 parameters: hidden | search | catalog | visible Try to set your _visibility to "visible" param.
With the last actualization of woocommerce, This code does not work. When I use this code : $my_post = array( 'post_title' => 'Variation #' . $i . ' of ' . esc_attr(strip_tags($_POST['postTitle'])), 'post_name' => 'product-' . $post_id . '-variation-' . $i, 'post_status' => 'publish', 'post_parent' => $post_id, 'post_type' => 'product_variation', 'guid' => home_url() . '/?product_variation=product-' . $post_id . '-variation-' . $i ); wp_insert_post( $my_post ); three variations are inserted by default, and I am not able to delete or insert another variation. Any solution?