/**
* Save variations
*
* @since 2.2
* @param int $id
* @param array $data
* @return bool
*/
protected function save_variations($id, $data)
{
global $wpdb;
$variations = $data['variations'];
$attributes = (array) maybe_unserialize(get_post_meta($id, '_product_attributes', true));
foreach ($variations as $menu_order => $variation) {
$variation_id = isset($variation['id']) ? absint($variation['id']) : 0;
// Generate a useful post title
$variation_post_title = sprintf(__('Variation #%s of %s', 'woocommerce'), $variation_id, esc_html(get_the_title($id)));
// Update or Add post
if (!$variation_id) {
$post_status = isset($variation['visible']) && false === $variation['visible'] ? 'private' : 'publish';
$new_variation = array('post_title' => $variation_post_title, 'post_content' => '', 'post_status' => $post_status, 'post_author' => get_current_user_id(), 'post_parent' => $id, 'post_type' => 'product_variation', 'menu_order' => $menu_order);
$variation_id = wp_insert_post($new_variation);
do_action('woocommerce_create_product_variation', $variation_id);
} else {
$update_variation = array('post_title' => $variation_post_title, 'menu_order' => $menu_order);
if (isset($variation['visible'])) {
$post_status = false === $variation['visible'] ? 'private' : 'publish';
$update_variation['post_status'] = $post_status;
}
$wpdb->update($wpdb->posts, $update_variation, array('ID' => $variation_id));
do_action('woocommerce_update_product_variation', $variation_id);
}
// Stop with we don't have a variation ID
if (is_wp_error($variation_id)) {
throw new WC_API_Exception('woocommerce_api_cannot_save_product_variation', $variation_id->get_error_message(), 400);
}
// SKU
if (isset($variation['sku'])) {
$sku = get_post_meta($variation_id, '_sku', true);
$new_sku = wc_clean($variation['sku']);
if ('' == $new_sku) {
update_post_meta($variation_id, '_sku', '');
} elseif ($new_sku !== $sku) {
if (!empty($new_sku)) {
$unique_sku = wc_product_has_unique_sku($variation_id, $new_sku);
if (!$unique_sku) {
throw new WC_API_Exception('woocommerce_api_product_sku_already_exists', __('The SKU already exists on another product', 'woocommerce'), 400);
} else {
update_post_meta($variation_id, '_sku', $new_sku);
}
} else {
update_post_meta($variation_id, '_sku', '');
}
}
}
// Thumbnail
if (isset($variation['image']) && is_array($variation['image'])) {
$image = current($variation['image']);
if ($image && is_array($image)) {
if (isset($image['position']) && isset($image['src']) && $image['position'] == 0) {
$upload = $this->upload_product_image(wc_clean($image['src']));
if (is_wp_error($upload)) {
throw new WC_API_Exception('woocommerce_api_cannot_upload_product_image', $upload->get_error_message(), 400);
}
$attachment_id = $this->set_product_image_as_attachment($upload, $id);
update_post_meta($variation_id, '_thumbnail_id', $attachment_id);
}
} else {
delete_post_meta($variation_id, '_thumbnail_id');
}
}
// Virtual variation
if (isset($variation['virtual'])) {
$is_virtual = true === $variation['virtual'] ? 'yes' : 'no';
update_post_meta($variation_id, '_virtual', $is_virtual);
}
// Downloadable variation
if (isset($variation['downloadable'])) {
$is_downloadable = true === $variation['downloadable'] ? 'yes' : 'no';
update_post_meta($variation_id, '_downloadable', $is_downloadable);
} else {
$is_downloadable = get_post_meta($variation_id, '_downloadable', true);
}
// Shipping data
$this->save_product_shipping_data($variation_id, $variation);
// Stock handling
if (isset($variation['managing_stock'])) {
$managing_stock = true === $variation['managing_stock'] ? 'yes' : 'no';
update_post_meta($variation_id, '_manage_stock', $managing_stock);
} else {
$managing_stock = get_post_meta($variation_id, '_manage_stock', true);
}
// Only update stock status to user setting if changed by the user, but do so before looking at stock levels at variation level
if (isset($variation['in_stock'])) {
$stock_status = true === $variation['in_stock'] ? 'instock' : 'outofstock';
wc_update_product_stock_status($variation_id, $stock_status);
}
if ('yes' === $managing_stock) {
if (isset($variation['backorders'])) {
if ('notify' == $variation['backorders']) {
$backorders = 'notify';
} else {
$backorders = true === $variation['backorders'] ? 'yes' : 'no';
}
} else {
$backorders = 'no';
}
update_post_meta($variation_id, '_backorders', $backorders);
if (isset($variation['stock_quantity'])) {
wc_update_product_stock($variation_id, wc_stock_amount($variation['stock_quantity']));
} else {
if (isset($data['inventory_delta'])) {
$stock_quantity = wc_stock_amount(get_post_meta($variation_id, '_stock', true));
$stock_quantity += wc_stock_amount($data['inventory_delta']);
wc_update_product_stock($variation_id, wc_stock_amount($stock_quantity));
}
}
} else {
delete_post_meta($variation_id, '_backorders');
delete_post_meta($variation_id, '_stock');
}
// Regular Price
if (isset($variation['regular_price'])) {
$regular_price = '' === $variation['regular_price'] ? '' : wc_format_decimal($variation['regular_price']);
update_post_meta($variation_id, '_regular_price', $regular_price);
} else {
$regular_price = get_post_meta($variation_id, '_regular_price', true);
}
// Sale Price
if (isset($variation['sale_price'])) {
$sale_price = '' === $variation['sale_price'] ? '' : wc_format_decimal($variation['sale_price']);
update_post_meta($variation_id, '_sale_price', $sale_price);
} else {
$sale_price = get_post_meta($variation_id, '_sale_price', true);
}
$date_from = isset($variation['sale_price_dates_from']) ? strtotime($variation['sale_price_dates_from']) : get_post_meta($variation_id, '_sale_price_dates_from', true);
$date_to = isset($variation['sale_price_dates_to']) ? strtotime($variation['sale_price_dates_to']) : get_post_meta($variation_id, '_sale_price_dates_to', true);
// Save Dates
if ($date_from) {
update_post_meta($variation_id, '_sale_price_dates_from', $date_from);
} else {
update_post_meta($variation_id, '_sale_price_dates_from', '');
}
if ($date_to) {
update_post_meta($variation_id, '_sale_price_dates_to', $date_to);
} else {
update_post_meta($variation_id, '_sale_price_dates_to', '');
}
if ($date_to && !$date_from) {
update_post_meta($variation_id, '_sale_price_dates_from', strtotime('NOW', current_time('timestamp')));
}
// Update price if on sale
if ('' != $sale_price && '' == $date_to && '' == $date_from) {
update_post_meta($variation_id, '_price', $sale_price);
} else {
update_post_meta($variation_id, '_price', $regular_price);
}
if ('' != $sale_price && $date_from && $date_from < strtotime('NOW', current_time('timestamp'))) {
update_post_meta($variation_id, '_price', $sale_price);
}
if ($date_to && $date_to < strtotime('NOW', current_time('timestamp'))) {
update_post_meta($variation_id, '_price', $regular_price);
update_post_meta($variation_id, '_sale_price_dates_from', '');
update_post_meta($variation_id, '_sale_price_dates_to', '');
}
// Tax class
if (isset($variation['tax_class'])) {
if ($variation['tax_class'] !== 'parent') {
update_post_meta($variation_id, '_tax_class', wc_clean($variation['tax_class']));
} else {
delete_post_meta($variation_id, '_tax_class');
}
}
// Downloads
if ('yes' == $is_downloadable) {
// Downloadable files
if (isset($variation['downloads']) && is_array($variation['downloads'])) {
$this->save_downloadable_files($id, $variation['downloads'], $variation_id);
}
// Download limit
if (isset($variation['download_limit'])) {
$download_limit = absint($variation['download_limit']);
update_post_meta($variation_id, '_download_limit', !$download_limit ? '' : $download_limit);
}
// Download expiry
if (isset($variation['download_expiry'])) {
$download_expiry = absint($variation['download_expiry']);
update_post_meta($variation_id, '_download_expiry', !$download_expiry ? '' : $download_expiry);
}
} else {
update_post_meta($variation_id, '_download_limit', '');
update_post_meta($variation_id, '_download_expiry', '');
update_post_meta($variation_id, '_downloadable_files', '');
}
// Update taxonomies
if (isset($variation['attributes'])) {
$updated_attribute_keys = array();
foreach ($variation['attributes'] as $attribute_key => $attribute) {
if (!isset($attribute['name'])) {
continue;
}
$_attribute = array();
if (isset($attribute['slug'])) {
$taxonomy = $this->get_attribute_taxonomy_by_slug($attribute['slug']);
}
if (!$taxonomy) {
$taxonomy = sanitize_title($attribute['name']);
}
if (isset($attributes[$taxonomy])) {
$_attribute = $attributes[$taxonomy];
}
if (isset($_attribute['is_variation']) && $_attribute['is_variation']) {
$_attribute_key = 'attribute_' . sanitize_title($_attribute['name']);
$updated_attribute_keys[] = $_attribute_key;
if (isset($_attribute['is_taxonomy']) && $_attribute['is_taxonomy']) {
// Don't use wc_clean as it destroys sanitized characters
$_attribute_value = isset($attribute['option']) ? sanitize_title(stripslashes($attribute['option'])) : '';
} else {
$_attribute_value = isset($attribute['option']) ? wc_clean(stripslashes($attribute['option'])) : '';
}
update_post_meta($variation_id, $_attribute_key, $_attribute_value);
}
}
// Remove old taxonomies attributes so data is kept up to date - first get attribute key names
$delete_attribute_keys = $wpdb->get_col($wpdb->prepare("SELECT meta_key FROM {$wpdb->postmeta} WHERE meta_key LIKE 'attribute_%%' AND meta_key NOT IN ( '" . implode("','", $updated_attribute_keys) . "' ) AND post_id = %d;", $variation_id));
foreach ($delete_attribute_keys as $key) {
delete_post_meta($variation_id, $key);
}
}
do_action('woocommerce_api_save_product_variation', $variation_id, $menu_order, $variation);
}
// Update parent if variable so price sorting works and stays in sync with the cheapest child
WC_Product_Variable::sync($id);
// Update default attributes options setting
if (isset($data['default_attribute'])) {
$data['default_attributes'] = $data['default_attribute'];
}
if (isset($data['default_attributes']) && is_array($data['default_attributes'])) {
$default_attributes = array();
foreach ($data['default_attributes'] as $default_attr_key => $default_attr) {
if (!isset($default_attr['name'])) {
continue;
}
$taxonomy = sanitize_title($default_attr['name']);
if (isset($default_attr['slug'])) {
$taxonomy = $this->get_attribute_taxonomy_by_slug($default_attr['slug']);
}
if (isset($attributes[$taxonomy])) {
$_attribute = $attributes[$taxonomy];
if ($_attribute['is_variation']) {
$value = '';
if (isset($default_attr['option'])) {
if ($_attribute['is_taxonomy']) {
// Don't use wc_clean as it destroys sanitized characters
$value = sanitize_title(trim(stripslashes($default_attr['option'])));
} else {
$value = wc_clean(trim(stripslashes($default_attr['option'])));
}
}
if ($value) {
$default_attributes[$taxonomy] = $value;
}
}
}
}
update_post_meta($id, '_default_attributes', $default_attributes);
}
return true;
}