Created
October 30, 2024 00:31
-
-
Save batonac/aa68537be27630f9ec08ac76b5deff43 to your computer and use it in GitHub Desktop.
WooCommerce Subscriptions Variations Fixer (after migration)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class SubscriptionVariationFixer { | |
private $proposed_changes = []; | |
private $total_items = 0; | |
private $total_issues = 0; | |
// Helper function to get the current site's attribute format from a variation | |
private function get_variation_attributes($variation_id) { | |
global $wpdb; | |
$attributes = []; | |
$var_attributes = $wpdb->get_results($wpdb->prepare(" | |
SELECT meta_key, meta_value | |
FROM {$wpdb->postmeta} | |
WHERE post_id = %d | |
AND meta_key LIKE 'attribute_pa_%' | |
", $variation_id)); | |
foreach ($var_attributes as $attr) { | |
// Convert attribute_pa_something to pa_something | |
$key = preg_replace('/^attribute_/', '', $attr->meta_key); | |
$attributes[$key] = $attr->meta_value; | |
} | |
return $attributes; | |
} | |
// Helper function to normalize keys and values | |
private function normalize_attribute($key, $value) { | |
// Handle key normalization | |
$normalized_key = preg_replace('/^pa_/', '', $key); | |
$normalized_key = ($normalized_key === 'package') ? 'packaging' : $normalized_key; | |
$normalized_key = 'pa_' . $normalized_key; | |
// Handle value normalization | |
$normalized_value = preg_replace('/-coffee$/', '', $value); | |
$normalized_value = preg_replace('/^whole-/', '', $normalized_value); | |
return [ | |
'key' => $normalized_key, | |
'value' => $normalized_value | |
]; | |
} | |
public function execute() { | |
global $wpdb; | |
try { | |
WP_CLI::log("Starting attribute normalization..."); | |
// First get all order items with their attributes | |
$items = $wpdb->get_results(" | |
SELECT | |
items.order_item_id, | |
items.order_id, | |
items.order_item_name, | |
meta_product.meta_value as product_id, | |
meta_variation.meta_value as variation_id | |
FROM {$wpdb->prefix}woocommerce_order_items as items | |
JOIN {$wpdb->prefix}wc_orders as orders ON items.order_id = orders.id | |
JOIN {$wpdb->prefix}woocommerce_order_itemmeta as meta_product | |
ON items.order_item_id = meta_product.order_item_id AND meta_product.meta_key = '_product_id' | |
LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as meta_variation | |
ON items.order_item_id = meta_variation.order_item_id AND meta_variation.meta_key = '_variation_id' | |
WHERE orders.type = 'shop_subscription' | |
AND items.order_item_type = 'line_item' | |
"); | |
foreach ($items as $item) { | |
$this->total_items++; | |
// Get all attribute meta for this item | |
$attribute_meta = $wpdb->get_results($wpdb->prepare(" | |
SELECT meta_id, meta_key, meta_value | |
FROM {$wpdb->prefix}woocommerce_order_itemmeta | |
WHERE order_item_id = %d | |
AND meta_key LIKE 'pa_%' | |
", $item->order_item_id)); | |
if (empty($attribute_meta)) { | |
continue; | |
} | |
// Get the correct attribute format from the current variation | |
$correct_attributes = $this->get_variation_attributes($item->variation_id); | |
$needs_update = false; | |
$updates = []; | |
foreach ($attribute_meta as $meta) { | |
$normalized = $this->normalize_attribute($meta->meta_key, $meta->meta_value); | |
// Check if the key needs updating | |
if ($meta->meta_key !== $normalized['key']) { | |
$needs_update = true; | |
$updates[] = [ | |
'meta_id' => $meta->meta_id, | |
'type' => 'key', | |
'old' => $meta->meta_key, | |
'new' => $normalized['key'] | |
]; | |
} | |
// Check if the value needs updating | |
if ($meta->meta_value !== $normalized['value']) { | |
$needs_update = true; | |
$updates[] = [ | |
'meta_id' => $meta->meta_id, | |
'type' => 'value', | |
'old' => $meta->meta_value, | |
'new' => $normalized['value'] | |
]; | |
} | |
} | |
if ($needs_update) { | |
$this->total_issues++; | |
$this->proposed_changes[] = [ | |
'item_id' => $item->order_item_id, | |
'subscription_id' => $item->order_id, | |
'product_name' => $item->order_item_name, | |
'updates' => $updates | |
]; | |
} | |
} | |
WP_CLI::log("\nAnalysis Summary:"); | |
WP_CLI::log("Total items processed: " . $this->total_items); | |
WP_CLI::log("Items with attribute issues: " . $this->total_issues); | |
WP_CLI::log("Items to update: " . count($this->proposed_changes)); | |
if (!empty($this->proposed_changes)) { | |
foreach ($this->proposed_changes as $change) { | |
WP_CLI::log(sprintf( | |
"\nSubscription #%d - %s", | |
$change['subscription_id'], | |
$change['product_name'] | |
)); | |
foreach ($change['updates'] as $update) { | |
WP_CLI::log(sprintf( | |
" %s update: %s → %s", | |
$update['type'], | |
$update['old'], | |
$update['new'] | |
)); | |
} | |
} | |
WP_CLI::confirm("Do you want to apply these changes?"); | |
$changes_made = 0; | |
foreach ($this->proposed_changes as $change) { | |
foreach ($change['updates'] as $update) { | |
if ($update['type'] === 'key') { | |
$wpdb->update( | |
$wpdb->prefix . 'woocommerce_order_itemmeta', | |
['meta_key' => $update['new']], | |
['meta_id' => $update['meta_id']] | |
); | |
} else { | |
$wpdb->update( | |
$wpdb->prefix . 'woocommerce_order_itemmeta', | |
['meta_value' => $update['new']], | |
['meta_id' => $update['meta_id']] | |
); | |
} | |
} | |
$changes_made++; | |
} | |
WP_CLI::success(sprintf("Successfully updated attributes for %d items!", $changes_made)); | |
} else { | |
WP_CLI::success("No attribute updates needed!"); | |
} | |
} catch (Exception $e) { | |
WP_CLI::error($e->getMessage()); | |
return 1; | |
} | |
} | |
} | |
// Run the fixer | |
$fixer = new SubscriptionVariationFixer(); | |
$fixer->execute(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class SubscriptionVariationFixer { | |
private $proposed_changes = []; | |
private $total_items = 0; | |
private $total_issues = 0; | |
// Helper function to normalize attribute value | |
private function normalize_attribute_value($value) { | |
return preg_replace('/-coffee$/', '', $value); | |
} | |
// Helper function to normalize attribute key | |
private function normalize_attribute_key($key) { | |
$key = preg_replace('/^(attribute_)?pa_/', '', $key); | |
return ($key === 'package') ? 'packaging' : $key; | |
} | |
// Helper function to check if attributes match | |
private function attributes_match($order_attributes, $variation_attributes) { | |
foreach ($order_attributes as $key => $value) { | |
$normalized_key = $this->normalize_attribute_key($key); | |
$normalized_value = $this->normalize_attribute_value($value); | |
$found_match = false; | |
foreach ($variation_attributes as $var_attr) { | |
if ($this->normalize_attribute_key($var_attr->meta_key) === $normalized_key) { | |
if ($this->normalize_attribute_value($var_attr->meta_value) === $normalized_value) { | |
$found_match = true; | |
break; | |
} | |
} | |
} | |
if (!$found_match) { | |
return false; | |
} | |
} | |
return true; | |
} | |
// Helper function to get correct attribute values from variation | |
private function get_variation_attribute_values($variation_id) { | |
global $wpdb; | |
$attributes = []; | |
$var_attributes = $wpdb->get_results($wpdb->prepare(" | |
SELECT meta_key, meta_value | |
FROM {$wpdb->postmeta} | |
WHERE post_id = %d | |
AND meta_key LIKE '%pa_%' | |
", $variation_id)); | |
foreach ($var_attributes as $attr) { | |
$key = preg_replace('/^attribute_/', '', $attr->meta_key); | |
$attributes[$key] = $attr->meta_value; | |
} | |
return $attributes; | |
} | |
public function execute() { | |
global $wpdb; | |
try { | |
WP_CLI::log("Starting subscription variation and attribute analysis..."); | |
// Get all subscriptions from HPOS | |
$subscriptions = $wpdb->get_results(" | |
SELECT id | |
FROM {$wpdb->prefix}wc_orders | |
WHERE type = 'shop_subscription' | |
"); | |
if (empty($subscriptions)) { | |
throw new Exception("No subscriptions found"); | |
} | |
foreach ($subscriptions as $subscription) { | |
// Get line items | |
$line_items = $wpdb->get_results($wpdb->prepare(" | |
SELECT * | |
FROM {$wpdb->prefix}woocommerce_order_items | |
WHERE order_id = %d | |
AND order_item_type = 'line_item' | |
", $subscription->id)); | |
foreach ($line_items as $item) { | |
$this->total_items++; | |
// Get item meta | |
$meta = $wpdb->get_results($wpdb->prepare(" | |
SELECT meta_key, meta_value | |
FROM {$wpdb->prefix}woocommerce_order_itemmeta | |
WHERE order_item_id = %d | |
", $item->order_item_id)); | |
// Extract product, variation IDs and attributes | |
$product_id = null; | |
$variation_id = null; | |
$attributes = []; | |
$attribute_meta_ids = []; | |
foreach ($meta as $m) { | |
if ($m->meta_key === '_product_id') { | |
$product_id = $m->meta_value; | |
} elseif ($m->meta_key === '_variation_id') { | |
$variation_id = $m->meta_value; | |
} elseif (strpos($m->meta_key, 'pa_') === 0) { | |
$attributes[$m->meta_key] = $m->meta_value; | |
$attribute_meta_ids[$m->meta_key] = $m->meta_id; | |
} | |
} | |
if (!$product_id || empty($attributes)) { | |
continue; | |
} | |
// Get all variations for this product | |
$variations = $wpdb->get_results($wpdb->prepare(" | |
SELECT ID | |
FROM {$wpdb->posts} | |
WHERE post_parent = %d | |
AND post_type = 'product_variation' | |
AND post_status = 'publish' | |
", $product_id)); | |
$matching_variation = null; | |
foreach ($variations as $var) { | |
$var_attributes = $wpdb->get_results($wpdb->prepare(" | |
SELECT meta_key, meta_value | |
FROM {$wpdb->postmeta} | |
WHERE post_id = %d | |
AND meta_key LIKE '%pa_%' | |
", $var->ID)); | |
if ($this->attributes_match($attributes, $var_attributes)) { | |
$matching_variation = $var->ID; | |
break; | |
} | |
} | |
if ($matching_variation) { | |
$needs_update = false; | |
$attribute_updates = []; | |
// Check if variation ID needs updating | |
if ($matching_variation !== $variation_id) { | |
$needs_update = true; | |
} | |
// Get correct attribute values from matching variation | |
$correct_attributes = $this->get_variation_attribute_values($matching_variation); | |
// Compare current attributes with correct ones | |
foreach ($attributes as $key => $value) { | |
$normalized_value = $this->normalize_attribute_value($value); | |
$correct_key = 'pa_' . $this->normalize_attribute_key($key); | |
if (isset($correct_attributes[$correct_key]) && | |
$normalized_value !== $correct_attributes[$correct_key]) { | |
$needs_update = true; | |
$attribute_updates[$key] = [ | |
'meta_id' => $attribute_meta_ids[$key], | |
'old_value' => $value, | |
'new_value' => $correct_attributes[$correct_key] | |
]; | |
} | |
} | |
if ($needs_update) { | |
$this->total_issues++; | |
$this->proposed_changes[] = [ | |
'item_id' => $item->order_item_id, | |
'subscription_id' => $subscription->id, | |
'product_id' => $product_id, | |
'old_variation_id' => $variation_id, | |
'new_variation_id' => $matching_variation, | |
'product_name' => $item->order_item_name, | |
'attribute_updates' => $attribute_updates | |
]; | |
} | |
} | |
} | |
} | |
WP_CLI::log("\nAnalysis Summary:"); | |
WP_CLI::log("Total items processed: " . $this->total_items); | |
WP_CLI::log("Items with variation or attribute issues: " . $this->total_issues); | |
WP_CLI::log("Fixes found: " . count($this->proposed_changes)); | |
if (!empty($this->proposed_changes)) { | |
foreach ($this->proposed_changes as $change) { | |
WP_CLI::log(sprintf( | |
"\nSubscription #%d - %s\nVariation ID: %d → %d", | |
$change['subscription_id'], | |
$change['product_name'], | |
$change['old_variation_id'], | |
$change['new_variation_id'] | |
)); | |
if (!empty($change['attribute_updates'])) { | |
WP_CLI::log("Attribute Updates:"); | |
foreach ($change['attribute_updates'] as $key => $update) { | |
WP_CLI::log(sprintf( | |
" %s: %s → %s", | |
$key, | |
$update['old_value'], | |
$update['new_value'] | |
)); | |
} | |
} | |
} | |
WP_CLI::confirm("Do you want to apply these changes?"); | |
// Apply changes | |
$changes_made = 0; | |
foreach ($this->proposed_changes as $change) { | |
// Update variation ID | |
$wpdb->update( | |
$wpdb->prefix . 'woocommerce_order_itemmeta', | |
['meta_value' => $change['new_variation_id']], | |
[ | |
'order_item_id' => $change['item_id'], | |
'meta_key' => '_variation_id' | |
] | |
); | |
// Update attributes | |
foreach ($change['attribute_updates'] as $key => $update) { | |
$wpdb->update( | |
$wpdb->prefix . 'woocommerce_order_itemmeta', | |
['meta_value' => $update['new_value']], | |
['meta_id' => $update['meta_id']] | |
); | |
} | |
$changes_made++; | |
WP_CLI::log(sprintf( | |
"Updated item #%d in subscription #%d", | |
$change['item_id'], | |
$change['subscription_id'] | |
)); | |
} | |
WP_CLI::success(sprintf("Successfully applied %d changes!", $changes_made)); | |
} else { | |
WP_CLI::success("No changes needed!"); | |
} | |
} catch (Exception $e) { | |
WP_CLI::error($e->getMessage()); | |
return 1; | |
} | |
} | |
} | |
// Run the fixer | |
$fixer = new SubscriptionVariationFixer(); | |
$fixer->execute(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment