I know this is probably super easy and I am overlooking something here.
I have a form on the front-end of our website where users can upload an image via a form and it creates a product in our shop. We are using Wordpress and Woocommerce but I felt this question was general enough to ask on here.
Currently everything works fine when uploading one image, How would I make this work for when a user uploads multiple images?
In the form I changed
<input type="file" name="thumbnail" id="thumbnail" />
to
<input type="file" name="thumbnail" id="thumbnail" multiple />and I thought wrapping the below in something like this would work:
if ($_FILES) {
foreach ($_FILES as $file => $array) {
// do necessary code
}
}
But it doesn't.
Here is what I have that creates the post. Im not sure why wrapping it in the foreach $FILES won't work?
$current_user = wp_get_current_user();
if(isset($_POST['submitted']) && isset($_POST['post_nonce_field']) && wp_verify_nonce($_POST['post_nonce_field'], 'post_nonce')) {
$postTitle = trim($_POST['postTitle']);
if($post_id)
{
wp_redirect(home_url());
exit;
}
//random sku
$skuu = rand();
$new_post = array(
'post_title' => $skuu,
'post_content' => '', //add anything here like link to vendor or whatever
'post_status' => 'publish',
'post_type' => 'product'
);
//insert and update post
$post_id = wp_insert_post($new_post);
update_post_meta($post_id, '_sku', $skuu );
if ($_FILES) {
foreach ($_FILES as $file => $array) {
$newupload = insert_attachment($file,$post_id);
}
}
//find available attributes
$avail_attributes = Array(
'high-resolution',
'medium-resolution',
'low-resolution'
);
//set terms (variations and attributes)
wp_set_object_terms ($post_id, 'variable', 'product_type');
wp_set_object_terms( $post_id, $avail_attributes, 'pa_resolution' );
//Categories
wp_set_object_terms( $post_id, array(esc_attr(strip_tags($_POST['postCat'])), esc_attr(strip_tags($_POST['raceCat']))), 'product_cat' );
// set variable data
$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', 'visible' );
update_post_meta( $post_id, '_stock_status', 'instock');
//update_post_meta( $post_id, '_product_vendors_commission', '25');
//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,
'post_author' => $vendor_data->term_id
);
// Insert the post into the database
wp_insert_post( $my_post );
$variable_id = $post_id + 2;
$variable_two = $variable_id + 1;
$variable_three = $variable_two + 1;
//get user prices
global $current_user;
$low_price = get_user_meta($current_user->ID, '_low_price', true);
$medium_price = get_user_meta($current_user->ID, '_medium_price', true);
$high_price = get_user_meta($current_user->ID, '_high_price', true);
//downloadable file paths
$download_id = get_post_thumbnail_id( $post_id );
$lowRes_src = wp_get_attachment_image_src( $download_id, 'low-resolution' );
$medRes_src = wp_get_attachment_image_src( $download_id, 'medium-resolution' );
$highRes_src = wp_get_attachment_image_src( $download_id, 'high-resolution' );
$low_downloadable_images = array();
$medium_downloadable_images = array();
$high_downloadable_images = array();
foreach ($lowRes_src as $lowRes_url) {
$lowRes_url = $lowRes_src[0];
$low_downloadable_images[md5( $lowRes_url )] = $lowRes_url;
}
foreach ($medRes_src as $medRes_url) {
$medRes_url = $medRes_src[0];
$medium_downloadable_images[md5( $medRes_url )] = $medRes_url;
}
foreach ($highRes_src as $highRes_url) {
$highRes_url = $highRes_src[0];
$high_downloadable_images[md5( $highRes_url )] = $highRes_url;
}
//echo $rand;
update_post_meta( $variable_id, 'attribute_pa_resolution', 'high-resolution');
update_post_meta( $variable_id, '_price', $high_price );
update_post_meta( $variable_id, '_regular_price', $high_price);
update_post_meta( $variable_id, '_virtual', 'yes');
update_post_meta( $variable_id, '_downloadable', 'yes');
update_post_meta( $variable_id, '_file_paths', $high_downloadable_images);
update_post_meta( $variable_two, 'attribute_pa_resolution', 'medium-resolution');
update_post_meta( $variable_two, '_price', $medium_price );
update_post_meta( $variable_two, '_regular_price', $medium_price);
update_post_meta( $variable_two, '_virtual', 'yes');
update_post_meta( $variable_two, '_downloadable', 'yes');
update_post_meta( $variable_two, '_file_paths', $medium_downloadable_images);
update_post_meta( $variable_three, 'attribute_pa_resolution', 'low-resolution');
update_post_meta( $variable_three, '_price', $low_price );
update_post_meta( $variable_three, '_regular_price', $low_price);
update_post_meta( $variable_three, '_virtual', 'yes');
update_post_meta( $variable_three, '_downloadable', 'yes');
update_post_meta( $variable_three, '_file_paths', $low_downloadable_images);
$i++;
}
}
//attach vendor to product
$vendor = get_user_vendor();
if( isset( $vendor->slug ) && strlen( $vendor->slug ) > 0 ) {
wp_set_object_terms( $post_id, $vendor->slug, 'shop_vendor', false );
}
In short I just need a way for the above code to create the post for each file uploaded, instead of just one file uploaded like it currently does.. Im all out of ideas
Got it, so I changed the name of the file input from thumbnail to thumbnail[]. Then I wrapped the above paste in a function and added this part at the top of the file and it works perfectly:
if ( $_FILES ) {
$files = $_FILES['thumbnail'];
foreach ($files['name'] as $key => $value) {
if ($files['name'][$key]) {
$file = array(
'name' => $files['name'][$key],
'type' => $files['type'][$key],
'tmp_name' => $files['tmp_name'][$key],
'error' => $files['error'][$key],
'size' => $files['size'][$key]
);
$_FILES = array("" => $file);
foreach ($_FILES as $file => $array) {
//call my function
create_var_product($file, $post_id);
}
}
}
}
Related
I'm trying to extract an xml feed for about 30k products.
In the lower one, the output reaches the level of 7-8 thousand and does not continue. I need to include all products in the output.
Can I split the process or output files into parts, would something like this require a lot of code?
Or if anyone has any other ideas, I'm open to that, as this seems to be an important issue I need to resolve.
I get data from database with WP_Query() loop starts with foreach and outputs with XMLWriter().
<?php
add_action( 'wp_loaded', 'product_feeds_output_writer');
function product_feeds_output_writer() {
$args = array(
'post_type' => 'product', // Total product 30k.
'post_status' => 'publish',
'posts_per_page' => -1,
'meta_query' => array(
array(
'key' => '_stock_status',
'value' => 'instock',
'compare' => '='
)
)
);
$query = new WP_Query();
if (! $posts = $query->query( $args ) ) {
$error = new WP_Error( '003', 'No products found.' );
wp_send_json_error( $error );
}
$bloginfo = array(
'name' => get_bloginfo('name'),
'url' => get_bloginfo('url'),
'description' => get_bloginfo('description')
);
$xml = new XMLWriter();
$xml->openURI( $_SERVER['DOCUMENT_ROOT'] . '/wp-content/uploads/woo-products-feed-generator/XMLWriter.xml' );
$xml->startDocument('1.0','UTF-8');
$xml->setIndent( 4 );
$xml->startElement('rss');
$xml->writeAttribute('version', '2.0');
$xml->writeAttribute('xmlns:g', 'http://base.google.com/ns/1.0');
$xml->startElement('channel');
$xml->writeElement('title', $bloginfo['name']);
$xml->writeElement('link', $bloginfo['url']);
$xml->writeElement('description', $bloginfo['description']);
foreach ( $posts as $post ) {
$product = wc_get_product( $post->ID );
$bir = random_int( 0, 9 );
$on = random_int( 10, 99 );
$yuz = random_int( 100, 999 );
$bin = random_int( 1000, 9999 );
$mil = random_int( 1000000, 9999999 );
$id = $product->get_id();
$title = $product->get_title();
$link = $product->get_permalink();
$description = $product->get_description();
$price = $product->get_regular_price() ?: $product->get_price();
$sale_price = $product->get_sale_price() ?: '';
$availability = $product->get_stock_status() == 'instock' ? 'in_stock' : 'out_of_stock';
$brand = $product->get_attribute( 'pa_marka' );
$image_link = $product->get_meta('_product_image_thumbnail') ?: wp_get_attachment_url( $product->get_image_id() );
$type = implode( '', wp_get_post_terms( $product->id, 'product_cat', array('fields' => 'names') ) );
$xml->startElement('item');
$xml->writeElement( 'g:id', $id );
$xml->writeElement( 'g:title', $title );
$xml->writeElement( 'g:description', $description );
$xml->writeElement( 'g:link', $link );
$xml->writeElement( 'g:price', $price.' '.'TRY' );
if( $sale_price ) {
$xml->writeElement( 'g:sale_price', $sale_price.' '.'TRY' );
}
$xml->writeElement( 'g:availability', $availability );
$xml->writeElement( 'g:brand', $brand );
//$xml->writeElement( 'g:google_product_category', $track );
$xml->writeElement( 'g:product_type', $type );
$xml->writeElement( 'g:identifier_exists', 'yes' );
$xml->writeElement( 'g:gtin', $mil.'-'.'K'.$yuz.'DK' );
$xml->writeElement( 'g:mpn', $bin.'BS'.'-'.'L'.$on.$bir.'ZP' );
$xml->writeElement( 'g:condition', 'Yeni' );
//$xml->writeElement( 'g:shipping', $track );
$xml->writeElement( 'g:image_link', $image_link );
if( $attachment_images = $product->get_gallery_image_ids() ) {
$attachment_images = array_slice( $attachment_images, 0, 10 );
foreach( $attachment_images as $attachment_id ) {
$image_link = wp_get_attachment_url( $attachment_id );
$xml->writeElement( 'g:additional_image_link', $image_link );
}
}
$xml->writeElement( 'g:material', 'Polipropilen' );
if( $value = $product->get_attribute( 'pa_renk' ) ) {
$xml->startElement('g:product_detail');
$xml->writeElement('g:section_name', 'Additional information' );
$xml->writeElement('g:attribute_name', 'Renk' );
$xml->writeElement('g:attribute_value', $value );
$xml->endElement();
}
if( $value = $product->get_attribute( 'pa_beden' ) ) {
$xml->startElement('g:product_detail');
$xml->writeElement('g:section_name', 'Additional information' );
$xml->writeElement('g:attribute_name', 'Beden' );
$xml->writeElement('g:attribute_value', $value );
$xml->endElement();
}
$xml->endElement();
}
$xml->endElement();
$xml->endElement();
$xml->endDocument();
$xml->flush();
unset($xml);
}
Would going through the WooCommerce REST API not be a better approach?
I believe the endpoint for products is :
/wp-json/wc/v3/products
You should be able to page jump through the page= url parameter:
/wp-json/wc/v3/products?page=1&per_page=5000
Parameter
Description
page
(integer) Current page of the collection. Default is 1.
per_page
(integer) Maximum number of items to be returned in result set. Default is 10.
#See https://woocommerce.github.io/woocommerce-rest-api-docs/#list-all-products
I've been racking my brain for a few days on this issue and can't figure out why my code to import is creating duplicate product categories.
Problem: I am pulling in about 30,000 products from the third party API in JSON format it is then is inserted into a custom database, from which I pull each product's data and format for importing into WooCommerce, these processes are working fine the problem comes when I am inserting the category terms. When categories are inserted using wp_insert_term some categories are duplicated but have a different URL slug.
ie...
— Ranges, Cooktops & Ovens
— — Range Hoods & Downdraft Ventilation (slug: range-hoods-downdraft-ventilation )
— — Range Hoods & Downdraft Ventilation (slug: range-hoods-downdraft-ventilation-ranges-cooktops-ovens)
The child category gets duplicated but with a different slug.
This is the Category array I'm looping through to create the hierarchical category paths:
"categoryPath":[{"id":"cat00000","name":"Company Name"},{"id":"abcat0900000","name":"Appliances"},{"id":"abcat0904000","name":"Ranges, Cooktops & Ovens"},{"id":"abcat0904002","name":"Range Hoods & Downdraft Ventilation"}]
Here is my single product import code the relevant category portion in at the bottom starting with $categoryPath, just wanted to give some context of what Im doing:
$result= $wpdb->get_var( "SELECT `results_array`
FROM $table_name
WHERE `arr_serialize2` = $textProductSku
AND `update_type` = 'create'
");
$final_product_formate_arrays = json_decode($result, true);
$post = array(
'post_title' => $final_product_formate_arrays['parent_or_simple_product']['title'],
'post_content' => $final_product_formate_arrays['parent_or_simple_product']['longDescription'],
'post_excerpt' => isset( $final_product_formate_arrays['parent_or_simple_product']['shortDescription']) ? $final_product_formate_arrays['parent_or_simple_product']['shortDescription'] : '' ,
'post_status' => "publish",
'post_parent' => '',
'post_type' => "product",
);
$post_id = wp_insert_post( $post );
if (!$post_id)
{
return false;
}
update_post_meta($post_id,'_complete_product_data',json_encode($final_product_formate_arrays));
update_post_meta($post_id, '_sku', $final_product_formate_arrays['parent_or_simple_product']['sku']);
update_post_meta( $post_id, '_visibility', 'visible' );
if($final_product_formate_arrays['parent_or_simple_product']['inStoreAvailability'] > 0)
{
update_post_meta( $post_id, '_manage_stock', "yes" );
update_post_meta( $post_id, '_stock', $final_product_formate_arrays['parent_or_simple_product']['stockQuantity'] );
update_post_meta( $post_id, '_stock_status', 'instock');
} elseif($final_product_formate_arrays['is_variation']){
update_post_meta( $post_id, '_manage_stock', "no" );
update_post_meta( $post_id, '_stock_status', 'instock');
} else {
update_post_meta( $post_id, '_manage_stock', "yes" );
update_post_meta( $post_id, '_stock_status', 'outofstock');
update_post_meta( $post_id, '_stock', 0 );
}
update_post_meta( $post_id, '_regular_price', $final_product_formate_arrays['parent_or_simple_product']['regularPrice'] );
update_post_meta( $post_id, '_sale_price', $final_product_formate_arrays['parent_or_simple_product']['salePrice'] );
update_post_meta( $post_id, '_price', $final_product_formate_arrays['parent_or_simple_product']['salePrice'] );
update_post_meta( $post_id, '_weight', $final_product_formate_arrays['parent_or_simple_product']['weight'] );
update_post_meta( $post_id, '_length', $final_product_formate_arrays['parent_or_simple_product']['depth'] );
update_post_meta( $post_id, '_width', $final_product_formate_arrays['parent_or_simple_product']['width'] );
update_post_meta( $post_id, '_height', $final_product_formate_arrays['parent_or_simple_product']['height'] );
update_post_meta( $post_id, '_wc_average_rating', $final_product_formate_arrays['parent_or_simple_product']['reviewAverage'] );
**$categoryPath = $final_product_formate_arrays['parent_or_simple_product']['categoryPath'];
if(!empty($categoryPath))
{
$parent_id='';
foreach ($categoryPath as $key => $value) {
if($key!=0) {
$new_str = str_replace( array( ',', '&' ), '-', $value['name'] );
$new_str = sanitize_title( $new_str );
$parent_term = term_exists( $parent_id, 'product_cat' ); // array is returned if taxonomy is given
if( isset( $parent_term )) {
$parent_id = $parent_term['term_id']; // get numeric term id
} else{
$parent_id = 0;
}
$term = wp_insert_term( $value['name'], 'product_cat', [
'description'=> $value['name'],
'parent' => intval( $parent_id ),
'slug' => $new_str,
]
);
if( is_wp_error( $term ) && isset( $term->error_data['term_exists'] ) )
{
$term_id = isset( $term->error_data['term_exists'] ) ? $term->error_data['term_exists'] :'';
$term_error = $term->get_error_message();
$term_error .="\n";
error_log($term_error, 3, $pluginlog);
} else if ( !is_wp_error( $term ) ) {
$new_term_id = isset( $term['term_id']) ? $term['term_id'] : '';
$term_id = $new_term_id;
}
$term_id = intval($term_id);
$parent_id = !empty($term_id)?$term_id:'';
wp_set_post_terms($post_id, $term_id, 'product_cat');**
// wp_set_object_terms($post_id, $term_id, 'product_cat');
} else {
continue;
}
}
}
I would like to set all variable products, when created, with the same six different variations using an already defined attribute ("bidragsstorlek"). The six variations should have different prices.
With my current code I succeded with all steps except the last one. I succefully added an attribute, six diffrent terms and six different variations. When I look in the back end panel the product variations have the prices I want, the only thing missing is to set the right term to each variation.
I know similar questions been asked before and been trying to implement those answers, but can't get it right.
$taxonomy = 'pa_bidragsstorlek';
$arrtermnames = array('50 kronor', '100 kronor', '250 kronor', '500 kronor', '1000 kronor', '5000 kronor');
$attr_data = Array(
$taxonomy=>Array(
'name' => $taxonomy,
'value' => '',
'is_visible' => '1',
'is_variation' => '1',
'is_taxonomy' => '1'
)
);
$newproduct = new WC_Product($product_id);
update_post_meta( $product_id, '_product_attributes', array_merge($newproduct->get_attributes(), $attr_data) );
foreach ($arrtermnames as &$value) {
$term_name = $value; // The term "NAME"
$term_slug = sanitize_title($term_name); // The term "slug"
// Check if the term exist and if not it create it (and get the term ID).
if( ! term_exists( $term_name, $taxonomy ) ){
$term_data = wp_insert_term( $term_name, $taxonomy );
$term_id = $term_data['term_id'];
} else {
$term_id = get_term_by( 'name', $term_name, $taxonomy )->term_id;
}
// get an instance of the WC_Product Object
$product = wc_get_product( $product_id );
$attributes = (array) $product->get_attributes();
// 1. If the product attribute is set for the product
if( array_key_exists( $taxonomy, $attributes ) ) {
foreach( $attributes as $key => $attribute ){
if( $key == $taxonomy ){
$options = (array) $attribute->get_options();
$options[] = $term_id;
$attribute->set_options($options);
$attributes[$key] = $attribute;
break;
}
}
$product->set_attributes( $attributes );
}
// 2. The product attribute is not set for the product
else {
$attribute = new WC_Product_Attribute();
$attribute->set_id( sizeof( $attributes) + 1 );
$attribute->set_name( $taxonomy );
$attribute->set_options( array( $term_id ) );
$attribute->set_position( sizeof( $attributes) + 1 );
$attribute->set_visible( true );
$attribute->set_variation( false );
$attributes[] = $attribute;
$product->set_attributes( $attributes );
}
$product->save();
// Append the new term in the product
if( ! has_term( $term_name, $taxonomy, $product_id ))
wp_set_object_terms($product_id, $term_slug, $taxonomy, true );
}
unset($value);
$parent_id = $product_id;
$arrprice = array(50, 100, 250, 500, 1000, 5000);
for ($x = 0; $x <= 5; $x++) {
$variation = array(
'post_title' => 'Product #' . $parent_id. $arrprice[$x],
'post_content' => '',
'post_status' => 'publish',
'post_parent' => $parent_id,
'post_type' => 'product_variation'
);
// The variation id
$variation_id = wp_insert_post( $variation );
update_post_meta( $variation_id, '_regular_price', $arrprice[$x] );
update_post_meta( $variation_id, '_price', $arrtermnames[$x] );
update_post_meta( $variation_id, '_virtual', '1' );
// Assign the size and color of this variation
update_post_meta( $variation_id, 'attribute_'.$taxonomy, $arrtermnames[$x] );
WC_Product_Variable::sync( $parent_id );
}
Right now with this code, when checking in the panel, I have a drop-down at each variation with all the terms - but none is choosen. Any idea what is missing to make each variation to be set to the right term?
Can you show me what I'm doing wrong?
My code to add a new product to Woocommerce looks like this right now:
function newProduct( $idERP, $titulo, $conteudo, $preco, $precoDesconto, $sku, $peso, $comprimento, $largura, $altura, $estoque, $variacoes, $categorias ){
global $woocommerce;
$titulo = utf8_encode($titulo);
$conteudo = utf8_encode($conteudo);
$my_post = array(
'post_title' => $titulo,
'post_content' => $conteudo,
'post_status' => 'pending',
'post_author' => 6,
'post_type' =>'product'
);
$product_ID = wp_insert_post( $my_post );
if ( $product_ID ){
if( $preco == $precoDesconto){
add_post_meta($product_ID, '_price', $precoDesconto );
}else{
add_post_meta($product_ID, '_regular_price', $preco );
add_post_meta($product_ID, '_price', $precoDesconto );
add_post_meta($product_ID, '_sale_price', $precoDesconto );
}
add_post_meta($product_ID, '_sku', $sku );
add_post_meta($product_ID, '_weight', $peso );
add_post_meta($product_ID, '_length', $comprimento );
add_post_meta($product_ID, '_width', $largura );
add_post_meta($product_ID, '_height', $altura );
add_post_meta($product_ID, '_stock', $this->estoque($estoque) );
add_post_meta($product_ID, '_idERP', $idERP ); //ID from erp
wp_set_object_terms( $product_ID, 'variable', 'product_type' );
//wp_set_object_terms( $product_ID, $categorias, 'product_cat');
$guardaVariacoes = array();$i = 1;
foreach($variacoes as $v){
$vid = wp_insert_post(
array(
'post_title' => 'Variacao #' . $i,
'post_name' => 'product-' . $product_ID . '-variation-' . $i,
'post_status' => 'publish',
'post_parent' => $product_ID,
'post_type' => 'product_variation',
'guid' => home_url() . '/?product_variation=product-' . $product_ID . '-variation-' . $i
)
);
if( $v[1] == $v[2] ){
update_post_meta( $vid, '_regular_price', $v[1]);
}else{
update_post_meta( $vid, '_price', $v[2] );
update_post_meta( $vid, '_regular_price', $v[1]);
update_post_meta( $vid, '_sale_price', $v[2] );
}
update_post_meta( $vid, '_stock', $this->estoque($v[0]) );
update_post_meta( $vid, '_weight', $v[3]);
update_post_meta( $vid, '_width', $v[4]);
update_post_meta( $vid, '_height', $v[5]);
update_post_meta( $vid, '_length', $v[6]);
update_post_meta( $vid, '_sku', $v[8]);
update_post_meta( $vid, '_idERP', $v[9]);
foreach($v[7] as $nAtr=>$vAtr){
$nomeAtributo = strtolower(trim($nAtr));
$tt = $nomeAtributo;
if(strtolower($tt) == 'tamanho')$tt = 'pa_tamanho';
if(strtolower($tt) == 'cor')$tt = 'pa_cor';
update_post_meta( $vid, 'attribute_'.$tt, trim($vAtr));
$temp = $tt.'|'.$nAtr;
if( !array_key_exists($temp,$guardaVariacoes) ) $guardaVariacoes[$temp] = array();
$guardaVariacoes[$temp][] = $vAtr;
}
$i++;
}
$product_attributes = array();
foreach($guardaVariacoes as $r=>$s){
$r = explode('|',$r);
$temp = array();
foreach($s as $w){
$w = trim($w);
if( strtoupper($w) == 'UNICA' ){
$w = 'Única';
}
if( strtoupper($w) == 'UNICO' ){
$w = 'Único';
}
if( array_search( $w,$temp)===false){
if( strtoupper(trim($r[1])) == 'COR'){
$temp[] = ucfirst(strtolower($w));
}else{
$temp[] = $w;
}
}
}
$s = array_unique($temp);
$tt = $r[1];
if(strtolower($tt) == 'tamanho')$tt = 'pa_tamanho';
if(strtolower($tt) == 'cor')$tt = 'pa_cor';
$product_attributes[$r[0]] = array(
'name' => $tt,
'value' => implode(' | ', $s ),
'position' => 1,
'is_visible' => 1,
'is_variation' => 1,
'is_taxonomy' => 1 //MY PROBLEM IS HERE
);
}
update_post_meta($product_ID, '_product_attributes', $product_attributes);
}
return $product_ID;
}
When I change the code like so...
'is_taxonomy' => 1
...it works, but it doesn't work for "search layered filters", which means I have to use taxonomy.
How do I do that without losing the "attributes" inside my product?
Sounds to me like you are trying to set Categories for products, and correct me if I'm wrong please. If this is the case, you don't want to use taxonomies. First, you are missing a ton of meta_keys for the product and all of it's variations. I suggest you install a fresh copy of WooCommerce plugin on a Fresh copy of Wordpress. Create a product manually in the back-end with 2 variations, and take a look at the wp_posts and wp_postmeta (post_id, meta_key, and meta_values). For variations, there are more values for the actual product id in the wp_postmeta table than that of the variations. _max_variation_sale_price_id, _max_variation_sale_price, _min_variation_sale_price_id, _min_variation_sale_price, etc. etc. etc. There is also another serialized array, _default_attributes, that you can just make an empty array and serialize it, for the product so that it has it's defaults set.
So, if you are referring to Categories and want to assign products to categories so that you can click on a category and see that product in there... than do the following:
Keep is_taxonomy set to 0
is_visible doesn't matter, if you want to see it under Additional Info, than set to 1, otherwise, it should still show all different variations, but I suppose it depends on your theme's way of handling this.
position needs to start at 1 and be incremented each time, so use a variable for this, example:
$position = 1;
foreach($guardaVariacoes as $r=>$s) {
// your top code... down to where the _product_attributes array begins...
$product_attributes[$r[0]] = array(
'name' => $tt,
'value' => implode(' | ', $s),
'position' => $position,
'is_visible' => count($s) > 1 ? 0 : 1,
'is_variation' => count($s) > 1 ? 1 : 0,
'is_taxonomy' => 0
);
$position++;
}
To create and assign categories on the fly for products you use: term_exists (will return 0 or NULL if term doesn't exist, you can check with empty()), wp_insert_term (if the term doesn't exist), insert it so that you can assign the category to the product. Example below if you want to insert a sub-brand category into a parent brand category, just an example of how I add categories to products (wp_term_relationships table assigns the categories to each product, via the products/posts ID value):
// Check if Brand exists or not...
$brand = term_exists('brand', 'product_cat', 0);
$category = 'new category';
$cat_name = addslashes(ucwords(strtolower($category)));
$cats['brand'] = array(
'name' => $cat_name,
'slug' => sanitize_title_with_dashes($category)
);
if (!empty($brand))
{
$subbrand = term_exists($cats['brand']['slug'], 'product_cat', $brand['term_id']);
if (empty($subbrand))
{
$subbrand = wp_insert_term(
$cats['brand']['name'],
'product_cat',
array(
'slug' => $cats['brand']['slug'],
'parent'=> $brand['term_id']
)
);
}
} else {
$brand = wp_insert_term(
'Brand',
'product_cat',
array(
'slug' => 'brand',
'parent'=> 0 // This means it will be at the first level!
)
);
$subbrand = term_exists($cats['brand']['slug'], 'product_cat', $brand['term_id']);
if (empty($subbrand))
{
$subbrand = wp_insert_term(
$cats['brand']['name'],
'product_cat',
array(
'slug' => $cats['brand']['slug'],
'parent'=> $brand['term_id']
)
);
}
}
$categories = array(
'product_id' => // put the product id value in here!
'terms' => array()
);
if (!empty($subbrand))
{
$categories['terms']['subbrands'] = $subbrand;
}
// Now insert into term_relationships table for that product and that's all folks...
foreach($categories['terms'] as $type => $nfo)
{
$wpdb->insert(
$wpdb->prefix . "term_relationships",
array(
'object_id' => $categories['product_id'],
'term_taxonomy_id' => intval($nfo['term_taxonomy_id']),
'term_order' => 0
),
array('%d', '%d', '%d')
);
}
So, hopefully this helps you to build categories for your products, while still keeping your product attributes in tact in the search filter. Good Luck.
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?