* : Nombre del componente a sincronizar (sin extensión .json) * * ## EJEMPLOS * * # Sincronizar top-notification-bar * wp roi-theme sync-component top-notification-bar * * # Sincronizar navbar * wp roi-theme sync-component navbar * * @param array $args Argumentos posicionales * @param array $assoc_args Argumentos asociativos (--flags) * @return void */ public function sync_component(array $args, array $assoc_args): void { if (empty($args[0])) { \WP_CLI::error('Debes especificar el nombre del componente. Ejemplo: wp roi-theme sync-component top-notification-bar'); return; } $component_name = $args[0]; $schemas_path = get_template_directory() . '/schemas'; $schema_file = $schemas_path . '/' . $component_name . '.json'; if (!file_exists($schema_file)) { \WP_CLI::error("Schema no encontrado: {$schema_file}"); return; } \WP_CLI::line(''); \WP_CLI::line("🔄 Sincronizando componente: {$component_name}"); \WP_CLI::line(''); $result = $this->syncSchemaToDatabase($schema_file, $component_name); \WP_CLI::line(''); if ($result['success']) { \WP_CLI::success($result['message']); \WP_CLI::line(''); \WP_CLI::line('📊 Estadísticas:'); \WP_CLI::line(' ├─ Campos insertados: ' . $result['stats']['inserted']); \WP_CLI::line(' ├─ Campos actualizados: ' . $result['stats']['updated']); \WP_CLI::line(' ├─ Campos eliminados (obsoletos): ' . $result['stats']['deleted']); \WP_CLI::line(' └─ Total en schema: ' . $result['stats']['total']); \WP_CLI::line(''); } else { \WP_CLI::error($result['message']); } } /** * Sincronizar todos los componentes desde schemas/ a la BD * * ## EJEMPLOS * * wp roi-theme sync-all-components * * @param array $args Argumentos posicionales * @param array $assoc_args Argumentos asociativos * @return void */ public function sync_all_components(array $args, array $assoc_args): void { $schemas_path = get_template_directory() . '/schemas'; if (!is_dir($schemas_path)) { \WP_CLI::error("Directorio de schemas no encontrado: {$schemas_path}"); return; } $schema_files = glob($schemas_path . '/*.json'); if (empty($schema_files)) { \WP_CLI::warning('No se encontraron schemas JSON en ' . $schemas_path); return; } \WP_CLI::line(''); \WP_CLI::line('🔄 Sincronizando todos los componentes...'); \WP_CLI::line(''); $total_stats = [ 'components' => 0, 'inserted' => 0, 'updated' => 0, 'total' => 0, 'errors' => 0 ]; foreach ($schema_files as $schema_file) { $component_name = basename($schema_file, '.json'); \WP_CLI::line(" → Procesando: {$component_name}"); $result = $this->syncSchemaToDatabase($schema_file, $component_name); if ($result['success']) { $total_stats['components']++; $total_stats['inserted'] += $result['stats']['inserted']; $total_stats['updated'] += $result['stats']['updated']; $total_stats['total'] += $result['stats']['total']; \WP_CLI::line(" ✓ {$result['message']}"); } else { $total_stats['errors']++; \WP_CLI::line(" ✗ Error: {$result['message']}"); } } \WP_CLI::line(''); \WP_CLI::success('Sincronización completada'); \WP_CLI::line(''); \WP_CLI::line('📊 Estadísticas totales:'); \WP_CLI::line(' ├─ Componentes sincronizados: ' . $total_stats['components']); \WP_CLI::line(' ├─ Campos insertados: ' . $total_stats['inserted']); \WP_CLI::line(' ├─ Campos actualizados: ' . $total_stats['updated']); \WP_CLI::line(' ├─ Total procesado: ' . $total_stats['total']); \WP_CLI::line(' └─ Errores: ' . $total_stats['errors']); \WP_CLI::line(''); } /** * Sincronizar un schema JSON a la base de datos * * @param string $schema_file Ruta completa al archivo JSON * @param string $component_name Nombre del componente * @return array{success: bool, message: string, stats: array{inserted: int, updated: int, deleted: int, total: int}} */ private function syncSchemaToDatabase(string $schema_file, string $component_name): array { global $wpdb; // Leer y decodificar JSON $json_content = file_get_contents($schema_file); if ($json_content === false) { return [ 'success' => false, 'message' => 'Error al leer archivo JSON', 'stats' => ['inserted' => 0, 'updated' => 0, 'deleted' => 0, 'total' => 0] ]; } $schema = json_decode($json_content, true); if ($schema === null) { return [ 'success' => false, 'message' => 'Error al decodificar JSON: ' . json_last_error_msg(), 'stats' => ['inserted' => 0, 'updated' => 0, 'deleted' => 0, 'total' => 0] ]; } $table = $wpdb->prefix . 'roi_theme_component_settings'; $stats = ['inserted' => 0, 'updated' => 0, 'deleted' => 0, 'total' => 0]; // Iterar grupos y campos if (!isset($schema['groups']) || !is_array($schema['groups'])) { return [ 'success' => false, 'message' => 'Schema inválido: falta clave "groups"', 'stats' => $stats ]; } // Construir lista de campos válidos del schema $validFields = []; foreach ($schema['groups'] as $group_name => $group_data) { if (!isset($group_data['fields']) || !is_array($group_data['fields'])) { continue; } foreach ($group_data['fields'] as $attribute_name => $field_config) { $validFields[] = $group_name . '::' . $attribute_name; } } // PASO 1: Eliminar registros obsoletos (que no están en el schema) $existing_records = $wpdb->get_results($wpdb->prepare( "SELECT id, group_name, attribute_name FROM {$table} WHERE component_name = %s", $component_name )); foreach ($existing_records as $record) { $key = $record->group_name . '::' . $record->attribute_name; if (!in_array($key, $validFields, true)) { $wpdb->delete($table, ['id' => $record->id], ['%d']); $stats['deleted']++; } } // PASO 2: Insertar/Actualizar campos del schema foreach ($schema['groups'] as $group_name => $group_data) { if (!isset($group_data['fields']) || !is_array($group_data['fields'])) { continue; } foreach ($group_data['fields'] as $attribute_name => $field_config) { $stats['total']++; // Verificar si el campo ya existe $existing = $wpdb->get_row($wpdb->prepare( "SELECT id, attribute_value FROM {$table} WHERE component_name = %s AND group_name = %s AND attribute_name = %s", $component_name, $group_name, $attribute_name )); $is_editable = isset($field_config['editable']) ? (bool)$field_config['editable'] : false; $default_value = isset($field_config['default']) ? $field_config['default'] : ''; // Convertir valor a string para almacenamiento if (is_array($default_value) || is_object($default_value)) { $default_value = json_encode($default_value); } elseif (is_bool($default_value)) { $default_value = $default_value ? '1' : '0'; } else { $default_value = (string)$default_value; } if ($existing === null) { // INSERT: Campo nuevo, usar default del JSON $result = $wpdb->insert( $table, [ 'component_name' => $component_name, 'group_name' => $group_name, 'attribute_name' => $attribute_name, 'attribute_value' => $default_value, 'is_editable' => $is_editable ? 1 : 0 ], ['%s', '%s', '%s', '%s', '%d'] ); if ($result !== false) { $stats['inserted']++; } } else { // UPDATE: Campo existente, preservar valor del usuario, solo actualizar is_editable $result = $wpdb->update( $table, ['is_editable' => $is_editable ? 1 : 0], [ 'component_name' => $component_name, 'group_name' => $group_name, 'attribute_name' => $attribute_name ], ['%d'], ['%s', '%s', '%s'] ); if ($result !== false) { $stats['updated']++; } } } } return [ 'success' => true, 'message' => "Componente '{$component_name}' sincronizado correctamente", 'stats' => $stats ]; } } // Registrar comando WP-CLI if (defined('WP_CLI') && WP_CLI) { \WP_CLI::add_command('roi-theme', MigrationCommand::class); }