0, 'profiles_migrated' => 0, 'terms_created' => 0, 'errors' => 0, 'skipped' => 0 ]; public static function run_migration($dry_run = false) { $migration_id = uniqid('taxonomy_migration_'); $start_time = microtime(true); self::log_message("Starting taxonomy migration (ID: {$migration_id})", 'info'); if ($dry_run) { self::log_message("Running in DRY RUN mode - no changes will be made", 'info'); } // Initialize migration tracking if (!$dry_run) { update_option('hvac_taxonomy_migration_status', [ 'id' => $migration_id, 'status' => 'in_progress', 'start_time' => time(), 'dry_run' => false ]); } try { // Step 1: Migrate trainer profiles self::migrate_trainer_profiles($dry_run); // Step 2: Migrate user meta to profiles for any remaining users self::migrate_remaining_user_meta($dry_run); // Step 3: Clean up old meta fields if (!$dry_run) { self::cleanup_old_meta_fields(); } // Complete migration if (!$dry_run) { self::complete_migration($migration_id); } $end_time = microtime(true); $duration = round($end_time - $start_time, 2); self::log_message("Taxonomy migration completed in {$duration} seconds", 'info'); self::log_message("Statistics: " . json_encode(self::$migration_stats), 'info'); } catch (Exception $e) { if (!$dry_run) { self::fail_migration($migration_id, $e->getMessage()); } self::log_message("Migration failed: " . $e->getMessage(), 'error'); throw $e; } return [ 'success' => true, 'stats' => self::$migration_stats, 'log' => self::$migration_log ]; } private static function migrate_trainer_profiles($dry_run = false) { // Get all trainer profiles $profiles = get_posts([ 'post_type' => 'trainer_profile', 'post_status' => 'publish', 'posts_per_page' => -1, 'meta_query' => [ 'relation' => 'OR', [ 'key' => '_taxonomy_migrated', 'compare' => 'NOT EXISTS' ], [ 'key' => '_taxonomy_migrated', 'value' => '1', 'compare' => '!=' ] ] ]); self::$migration_stats['total_profiles'] = count($profiles); self::log_message("Found " . count($profiles) . " trainer profiles to migrate", 'info'); foreach ($profiles as $profile) { try { self::migrate_profile_to_taxonomies($profile, $dry_run); } catch (Exception $e) { self::$migration_stats['errors']++; self::log_message("Error migrating profile {$profile->ID}: " . $e->getMessage(), 'error'); } } } private static function migrate_profile_to_taxonomies($profile, $dry_run = false) { $profile_id = $profile->ID; self::log_message("Migrating profile {$profile_id} ({$profile->post_title})", 'info'); if (!$dry_run) { // Check if already migrated if (get_post_meta($profile_id, '_taxonomy_migrated', true) === '1') { self::log_message("Profile {$profile_id} already migrated, skipping", 'info'); self::$migration_stats['skipped']++; return; } } $taxonomy_mappings = [ 'business_type' => 'business_type', 'training_audience' => 'training_audience', 'training_formats' => 'training_formats', 'training_locations' => 'training_locations', 'training_resources' => 'training_resources' ]; $migrated_any = false; foreach ($taxonomy_mappings as $meta_key => $taxonomy) { // Get existing meta value $meta_value = get_post_meta($profile_id, $meta_key, true); if (empty($meta_value)) { continue; } if ($dry_run) { self::log_message("DRY RUN: Would migrate {$meta_key} for profile {$profile_id}: " . (is_array($meta_value) ? implode(', ', $meta_value) : $meta_value), 'info'); $migrated_any = true; continue; } // Process the meta value $terms_to_assign = []; if (is_array($meta_value)) { $term_names = $meta_value; } elseif (is_string($meta_value)) { $term_names = array_map('trim', explode(',', $meta_value)); } else { continue; } // Find or create terms foreach ($term_names as $term_name) { if (empty($term_name)) { continue; } $term = get_term_by('name', $term_name, $taxonomy); if (!$term) { // Create the term $result = wp_insert_term($term_name, $taxonomy); if (!is_wp_error($result)) { $terms_to_assign[] = $result['term_id']; self::$migration_stats['terms_created']++; self::log_message("Created new term '{$term_name}' in taxonomy '{$taxonomy}'", 'info'); } else { self::log_message("Error creating term '{$term_name}': " . $result->get_error_message(), 'error'); } } else { $terms_to_assign[] = $term->term_id; } } // Assign terms to profile if (!empty($terms_to_assign)) { $result = wp_set_post_terms($profile_id, $terms_to_assign, $taxonomy, false); if (!is_wp_error($result)) { self::log_message("Assigned " . count($terms_to_assign) . " terms to {$taxonomy} for profile {$profile_id}", 'info'); $migrated_any = true; // Remove the old meta field delete_post_meta($profile_id, $meta_key); } else { self::log_message("Error assigning terms to {$taxonomy}: " . $result->get_error_message(), 'error'); } } } if ($migrated_any && !$dry_run) { // Mark as migrated update_post_meta($profile_id, '_taxonomy_migrated', '1'); update_post_meta($profile_id, '_taxonomy_migration_date', current_time('mysql')); self::$migration_stats['profiles_migrated']++; } } private static function migrate_remaining_user_meta($dry_run = false) { // Find users with trainer roles who have taxonomy data in user meta $users = get_users([ 'role__in' => ['hvac_trainer', 'hvac_master_trainer'], 'meta_query' => [ 'relation' => 'OR', [ 'key' => 'training_audience', 'compare' => 'EXISTS' ], [ 'key' => 'training_formats', 'compare' => 'EXISTS' ], [ 'key' => 'training_locations', 'compare' => 'EXISTS' ], [ 'key' => 'training_resources', 'compare' => 'EXISTS' ], [ 'key' => 'business_type', 'compare' => 'EXISTS' ] ] ]); self::log_message("Found " . count($users) . " users with taxonomy data in user meta", 'info'); foreach ($users as $user) { try { self::migrate_user_meta_to_profile($user, $dry_run); } catch (Exception $e) { self::$migration_stats['errors']++; self::log_message("Error migrating user meta for user {$user->ID}: " . $e->getMessage(), 'error'); } } } private static function migrate_user_meta_to_profile($user, $dry_run = false) { $user_id = $user->ID; // Get or create trainer profile $profile_id = get_user_meta($user_id, 'trainer_profile_id', true); if (!$profile_id) { if ($dry_run) { self::log_message("DRY RUN: Would create trainer profile for user {$user_id}", 'info'); return; } // Create trainer profile $profile_manager = HVAC_Trainer_Profile_Manager::get_instance(); $profile_id = $profile_manager->create_trainer_profile($user_id); if (!$profile_id) { throw new Exception("Failed to create trainer profile for user {$user_id}"); } self::log_message("Created trainer profile {$profile_id} for user {$user_id}", 'info'); } $taxonomy_fields = ['business_type', 'training_audience', 'training_formats', 'training_locations', 'training_resources']; foreach ($taxonomy_fields as $field) { $value = get_user_meta($user_id, $field, true); if (!empty($value)) { if ($dry_run) { self::log_message("DRY RUN: Would migrate {$field} from user {$user_id} to profile {$profile_id}", 'info'); continue; } // Use the profile manager's taxonomy update method $profile_manager = HVAC_Trainer_Profile_Manager::get_instance(); $profile_manager->update_profile($profile_id, [$field => $value]); // Remove from user meta delete_user_meta($user_id, $field); self::log_message("Migrated {$field} from user {$user_id} to profile {$profile_id}", 'info'); } } } private static function cleanup_old_meta_fields() { global $wpdb; $taxonomy_fields = ['business_type', 'training_audience', 'training_formats', 'training_locations', 'training_resources']; foreach ($taxonomy_fields as $field) { // Clean up user meta $deleted_user_meta = $wpdb->delete( $wpdb->usermeta, ['meta_key' => $field], ['%s'] ); // Clean up post meta (from profiles) $deleted_post_meta = $wpdb->delete( $wpdb->postmeta, ['meta_key' => $field], ['%s'] ); if ($deleted_user_meta > 0 || $deleted_post_meta > 0) { self::log_message("Cleaned up {$field}: {$deleted_user_meta} user meta entries, {$deleted_post_meta} post meta entries", 'info'); } } } private static function complete_migration($migration_id) { update_option('hvac_taxonomy_migration_status', [ 'id' => $migration_id, 'status' => 'completed', 'end_time' => time(), 'stats' => self::$migration_stats ]); } private static function fail_migration($migration_id, $error_message) { update_option('hvac_taxonomy_migration_status', [ 'id' => $migration_id, 'status' => 'failed', 'end_time' => time(), 'error_message' => $error_message, 'stats' => self::$migration_stats ]); } private static function log_message($message, $level = 'info') { $timestamp = current_time('mysql'); $log_entry = "[{$timestamp}] [{$level}] {$message}"; self::$migration_log[] = $log_entry; // Also log to WordPress error log if it's an error if ($level === 'error') { error_log("HVAC Taxonomy Migration: " . $message); } } /** * Get migration status */ public static function get_migration_status() { return get_option('hvac_taxonomy_migration_status', ['status' => 'not_started']); } /** * Reset migration status (for testing) */ public static function reset_migration_status() { delete_option('hvac_taxonomy_migration_status'); // Remove migration flags from all profiles global $wpdb; $wpdb->delete( $wpdb->postmeta, ['meta_key' => '_taxonomy_migrated'], ['%s'] ); $wpdb->delete( $wpdb->postmeta, ['meta_key' => '_taxonomy_migration_date'], ['%s'] ); } }