I have over 400 products in my WooComerce store, and they all were done as a simple product. The problem is that now I have to add variations to all the products (to satify a shipping plugin) and it will take me forever if I have to do this manually for each product.
I have setup one of my product with all the variations and values needed to be duplicated to the other products of the same category.
I tried piecing together a simple plugin to do this based on TSCAmerica.com answer.
Below is the complete plugin code for anyone to try out and see if it is working for them or where it needs to be fixed:
<?php
/*
Plugin Name: WC Copy Variations
Plugin URI: https://www.example.com
Description: Copy a Woocommerce Product's Variations From One Product to Another
Version: 1.0
Author: Wayne Schulz
Author URI: https://www.example.com
License: GPL2
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: wc-copy-variations
Domain Path: /languages
*/
// plugin settings menu
add_action('admin_menu', 'wcvc_create_menu');
function wcvc_create_menu() {
//create new top-level menu
add_menu_page('WC Copy Variations Settings', 'Copy Variations', 'administrator', __FILE__, 'wcvc_settings_page' , plugins_url('/images/icon.png', __FILE__) );
//call register settings function
add_action( 'admin_init', 'register_wcvc_settings' );
}
function register_wcvc_settings() {
//register our settings
register_setting( 'wcvc-settings-group', 'source_product_id' );
register_setting( 'wcvc-settings-group', 'target_product_id' );
register_setting( 'wcvc-settings-group', 'wcvc_status' );
}
function wcvc_settings_page() {
// Trigger wcv copy
$wcvc_status = get_option('wcvc_status');
if(str_contains($wcvc_status, 'yes')) {
perform_wcv_copy();
}
?>
<div class="wrap">
<h1>WC Copy Variations</h1>
<form method="post" action="options.php">
<?php settings_fields( 'wcvc-settings-group' ); ?>
<?php do_settings_sections( 'wcvc-settings-group' ); ?>
<table class="form-table">
<tr valign="top">
<th scope="row">Source Product ID</th>
<td><input type="text" name="source_product_id" value="<?php echo esc_attr( get_option('source_product_id') ); ?>" /></td>
</tr>
<tr valign="top">
<th scope="row">Target Product ID</th>
<td><input type="text" name="target_product_id" value="<?php echo esc_attr( get_option('target_product_id') ); ?>" /></td>
</tr>
<tr valign="top">
<th scope="row">Type 'yes' to copy</th>
<td><input type="text" name="wcvc_status" value="<?php echo esc_attr( get_option('wcvc_status') ); ?>" /></td>
</tr>
</table>
<?php submit_button(); ?>
</form>
</div>
<table><tr><td style="vertical-align: top;"><?php show_wcv_source(); ?></td><td style="vertical-align: top;"><?php show_wcv_target(); ?></td></tr></table>
<?php }
function perform_wcv_copy() {
global $options;
$source_product_id = get_option('source_product_id');
$target_product_id = get_option('target_product_id');
$source_product = wc_get_product($source_product_id);
// Check if the source product has variations
if ($source_product->is_type('variable')) {
// Get the variations of the source product
$variations = $source_product->get_children();
foreach ($variations as $variation_id) {
$variation = wc_get_product($variation_id);
// Create a new variation for the target product
$new_variation = new WC_Product_Variation();
$new_variation->set_parent_id($target_product_id);
// Set attributes and other settings from the source variation
$new_variation->set_attributes($variation->get_attributes());
$new_variation->set_regular_price($variation->get_regular_price());
$new_variation->set_sale_price($variation->get_sale_price());
$new_variation->set_weight($variation->get_weight());
$new_variation->set_length($variation->get_length());
$new_variation->set_height($variation->get_height());
$new_variation->set_width($variation->get_width());
// Save the new variation
$new_variation->save();
}
}
update_option( 'wcvc_status', 'done' );
}
function show_wcv_source() {
global $options;
$source_product_id = get_option('source_product_id');
$target_product_id = get_option('target_product_id');
echo '<p><strong>Source Product ID: '.$source_product_id.'</strong><br />';
echo 'Product Categories: '.wc_get_product_category_list($source_product_id).'</p>';
$source_product = wc_get_product($source_product_id);
// Check if the source product has variations
if ($source_product->is_type('variable')) {
// Get the variations of the source product
$s_variations = $source_product->get_children();
foreach ($s_variations as $s_variation_id) {
$s_variation = wc_get_product($s_variation_id);
echo '<div class="wrap"><p>';
//$raw_variation = var_export($variation, true);
//echo 'Variation ID: '.$raw_variation.'<br />';
echo 'Attributes: ';
var_dump($s_variation->get_attributes());
echo '<br />';
$s_regular_price = $s_variation->get_regular_price();
echo 'Regular Price: '.$s_regular_price.'<br />';
$s_sale_price = $s_variation->get_sale_price();
echo 'Sale Price: '.$s_sale_price.'<br />';
$s_weight = $s_variation->get_weight();
echo 'Weight: '.$s_weight.'<br />';
$s_length = $s_variation->get_length();
echo 'Length: '.$s_length.'<br />';
$s_height = $s_variation->get_height();
echo 'Height: '.$s_height.'<br />';
$s_width = $s_variation->get_width();
echo 'Width: '.$s_width.'<br />';
echo '</p></div>';
}
}
}
function show_wcv_target() {
global $options;
$source_product_id = get_option('source_product_id');
$target_product_id = get_option('target_product_id');
echo '<p><strong>Target Product ID: '.$target_product_id.'</strong><br />';
echo 'Product Categories: '.wc_get_product_category_list($target_product_id).'</p>';
$target_product = wc_get_product($target_product_id);
// Check if the target product has variations
if ($target_product->is_type('variable')) {
// Get the variations of the target product (if any)
$t_variations = $target_product->get_children();
foreach ($t_variations as $t_variation_id) {
$t_variation = wc_get_product($t_variation_id);
echo '<div class="wrap"><p>';
// $raw_variation = var_export($variation, true);
// echo 'Variation ID: '.$raw_variation.'<br />';
echo 'Attributes: ';
var_dump($t_variation->get_attributes());
echo '<br />';
$t_regular_price = $t_variation->get_regular_price();
echo 'Regular Price: '.$t_regular_price.'<br />';
$t_sale_price = $t_variation->get_sale_price();
echo 'Sale Price: '.$t_sale_price.'<br />';
$t_weight = $t_variation->get_weight();
echo 'Weight: '.$t_weight.'<br />';
$t_length = $t_variation->get_length();
echo 'Length: '.$t_length.'<br />';
$t_height = $t_variation->get_height();
echo 'Height: '.$t_height.'<br />';
$t_width = $t_variation->get_width();
echo 'Width: '.$t_width.'<br />';
echo '</p></div>';
}
}
}
?>
Enter the source ID and the target ID and hit save, then it will dispaly any variations in the source and target products. If you want to perform the copy, enter ‘yes’ in the last field and hit save again.
The issue I am having is that it is not saving the new variations to the target product. I VAR DUMPED every possible variable and the data is there in the right places. After the save command, the ID does increment for each new variation, however, either the save() is not working properly for some reason or WooComerce is deleting the saved variation immediately after the save.
What am I missing or doing wrong?
2
Answers
After Moshe Gross's answer, I added a cleanup routine to the perform_wvc_copy function, removing any existing product variations before the copy.
The fixed function:
If there's anything else, please provide a comment or answer.
There’s two things missing from the code above
You’re not adding the attribute to the parent product, and the product is still set as a simple (not variable) product
you can add a few lines to fix this
P.s. if you run this for the same id multiple times (and you might have done already even if the variations didn’t show) you’ll have multiple of the same variations