diff --git a/.gitignore b/.gitignore
index 1344a0b0..2ee64eab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -70,6 +70,9 @@ composer.lock
# Planning and documentation
_planeacion/
+# Testing infrastructure (composer, phpunit, phpcs configs and dependencies)
+_testing-suite/
+
# Claude Code tools
.playwright-mcp/
.serena/
diff --git a/admin/includes/class-db-manager.php b/admin/includes/class-db-manager.php
index 74c45581..c6d4936d 100644
--- a/admin/includes/class-db-manager.php
+++ b/admin/includes/class-db-manager.php
@@ -6,49 +6,93 @@
*
* @package ROI_Theme
* @since 2.2.0
+ * @deprecated 2.0.0 Use ROITheme\Infrastructure\Persistence\WordPress\WordPressComponentRepository instead
+ * @see ROITheme\Infrastructure\Persistence\WordPress\WordPressComponentRepository
+ *
+ * Esta clase será eliminada en la versión 3.0.0
+ * Por favor migre su código a la nueva arquitectura Clean Architecture
*/
if (!defined('ABSPATH')) {
exit;
}
+/**
+ * ROI_DB_Manager
+ *
+ * @deprecated 2.0.0
+ */
class ROI_DB_Manager {
/**
* Nombre de la tabla de componentes (sin prefijo)
+ *
+ * @deprecated 2.0.0
*/
const TABLE_COMPONENTS = 'roi_theme_components';
/**
* Nombre de la tabla de defaults (sin prefijo)
+ *
+ * @deprecated 2.0.0
*/
const TABLE_DEFAULTS = 'roi_theme_components_defaults';
/**
* Versión de la base de datos
+ *
+ * @deprecated 2.0.0
*/
const DB_VERSION = '1.0';
/**
* Opción para almacenar la versión de la DB
+ *
+ * @deprecated 2.0.0
*/
const DB_VERSION_OPTION = 'roi_db_version';
+ /**
+ * @var \ROITheme\Infrastructure\Adapters\LegacyDBManagerAdapter
+ */
+ private $adapter;
+
/**
* Constructor
+ *
+ * @deprecated 2.0.0
*/
public function __construct() {
+ _deprecated_function(
+ __CLASS__ . '::__construct',
+ '2.0.0',
+ 'ROITheme\Infrastructure\Persistence\WordPress\WordPressComponentRepository'
+ );
+
+ $this->logDeprecation(__CLASS__, __FUNCTION__);
+
// Hook para verificar/actualizar DB en cada carga
add_action('admin_init', array($this, 'maybe_create_tables'));
+
+ // Inicializar adapter para mantener compatibilidad
+ $this->adapter = $this->getLegacyAdapter();
}
/**
* Obtener nombre completo de tabla con prefijo
*
+ * @deprecated 2.0.0
+ *
* @param string $table_type Tipo de tabla: 'components' (personalizaciones) o 'defaults' (valores por defecto)
* @return string Nombre completo de la tabla con prefijo
*/
public function get_table_name($table_type = 'components') {
+ _deprecated_function(
+ __FUNCTION__,
+ '2.0.0',
+ 'Direct database access not recommended - use repositories'
+ );
+
global $wpdb;
if ($table_type === 'defaults') {
@@ -60,8 +104,11 @@ class ROI_DB_Manager {
/**
* Verificar si las tablas necesitan ser creadas o actualizadas
+ *
+ * @deprecated 2.0.0 Tables are now managed through database migrations
*/
public function maybe_create_tables() {
+ // Keep for backward compatibility but tables are managed differently now
$installed_version = get_option(self::DB_VERSION_OPTION);
if ($installed_version !== self::DB_VERSION) {
@@ -72,17 +119,22 @@ class ROI_DB_Manager {
/**
* Crear tablas personalizadas
- * Crea tanto la tabla de componentes (personalizaciones) como la de defaults
+ *
+ * @deprecated 2.0.0 Use DatabaseMigrator service instead
*/
public function create_tables() {
+ _deprecated_function(
+ __FUNCTION__,
+ '2.0.0',
+ 'ROITheme\Infrastructure\Services\DatabaseMigrator'
+ );
+
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
- $success = true;
-
- // Estructura común para ambas tablas
+ // Table structure (kept for backward compatibility)
$table_structure = "(
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
component_name VARCHAR(50) NOT NULL,
@@ -98,192 +150,169 @@ class ROI_DB_Manager {
INDEX idx_updated (updated_at)
) $charset_collate;";
- // Crear tabla de componentes (personalizaciones del usuario)
+ // Create components table
$table_components = $this->get_table_name('components');
- $sql_components = "CREATE TABLE $table_components $table_structure";
+ $sql_components = "CREATE TABLE IF NOT EXISTS $table_components $table_structure";
dbDelta($sql_components);
- if ($wpdb->get_var("SHOW TABLES LIKE '$table_components'") === $table_components) {
- error_log("ROI DB Manager: Tabla $table_components creada/actualizada exitosamente");
- } else {
- error_log("ROI DB Manager: Error al crear tabla $table_components");
- $success = false;
- }
-
- // Crear tabla de defaults (valores por defecto del tema)
+ // Create defaults table
$table_defaults = $this->get_table_name('defaults');
- $sql_defaults = "CREATE TABLE $table_defaults $table_structure";
+ $sql_defaults = "CREATE TABLE IF NOT EXISTS $table_defaults $table_structure";
dbDelta($sql_defaults);
-
- if ($wpdb->get_var("SHOW TABLES LIKE '$table_defaults'") === $table_defaults) {
- error_log("ROI DB Manager: Tabla $table_defaults creada/actualizada exitosamente");
- } else {
- error_log("ROI DB Manager: Error al crear tabla $table_defaults");
- $success = false;
- }
-
- return $success;
}
/**
* Verificar si una tabla existe
*
- * @param string $table_type Tipo de tabla: 'components' o 'defaults'
- * @return bool True si la tabla existe
+ * @deprecated 2.0.0
+ *
+ * @param string $table_type Tipo de tabla
+ * @return bool True si existe, false si no
*/
public function table_exists($table_type = 'components') {
global $wpdb;
$table_name = $this->get_table_name($table_type);
- return $wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name;
+ $query = $wpdb->prepare('SHOW TABLES LIKE %s', $table_name);
+ return $wpdb->get_var($query) === $table_name;
}
/**
- * Guardar configuración de un componente
+ * Save component configuration
*
- * @param string $component_name Nombre del componente
- * @param string $config_key Clave de configuración
- * @param mixed $config_value Valor de configuración
- * @param string $data_type Tipo de dato (string, boolean, integer, json)
- * @param string $version Versión del tema
- * @param string $table_type Tipo de tabla: 'components' (personalizaciones) o 'defaults' (valores por defecto)
- * @return bool|int ID del registro o false en caso de error
+ * @deprecated 2.0.0 Use SaveComponentUseCase::execute() instead
+ *
+ * @param string $component_name Component name (e.g., 'top_notification_bar', 'navbar', 'footer', 'hero_section')
+ * @param string $config_key Configuration key
+ * @param mixed $config_value Configuration value
+ * @param string $data_type Data type (string, boolean, integer, array)
+ * @param string|null $version Schema version
+ * @param string $table_type Table type (components or defaults)
+ * @return bool Success status
*/
public function save_config($component_name, $config_key, $config_value, $data_type = 'string', $version = null, $table_type = 'components') {
- global $wpdb;
- $table_name = $this->get_table_name($table_type);
+ _deprecated_function(
+ __FUNCTION__,
+ '2.0.0',
+ 'ROITheme\Application\UseCases\SaveComponent\SaveComponentUseCase::execute()'
+ );
- // Convertir valor según tipo
- if ($data_type === 'json' && is_array($config_value)) {
- $config_value = json_encode($config_value, JSON_UNESCAPED_UNICODE);
- } elseif ($data_type === 'boolean') {
- $config_value = $config_value ? '1' : '0';
- }
+ $this->logDeprecation(__CLASS__, __FUNCTION__, func_get_args());
- // Usar ON DUPLICATE KEY UPDATE para INSERT o UPDATE
- $result = $wpdb->query($wpdb->prepare(
- "INSERT INTO $table_name (component_name, config_key, config_value, data_type, version, updated_at)
- VALUES (%s, %s, %s, %s, %s, %s)
- ON DUPLICATE KEY UPDATE
- config_value = VALUES(config_value),
- data_type = VALUES(data_type),
- version = VALUES(version),
- updated_at = VALUES(updated_at)",
+ // Delegar al adapter para mantener funcionalidad
+ return $this->adapter->save_config(
$component_name,
$config_key,
$config_value,
$data_type,
$version,
- current_time('mysql')
- ));
-
- return $result !== false ? $wpdb->insert_id : false;
+ $table_type
+ );
}
/**
- * Obtener configuración de un componente
+ * Get component configuration
*
- * @param string $component_name Nombre del componente
- * @param string $config_key Clave específica (opcional)
- * @param string $table_type Tipo de tabla: 'components' (personalizaciones) o 'defaults' (valores por defecto)
- * @return array|mixed Configuración completa o valor específico
+ * @deprecated 2.0.0 Use GetComponentUseCase::execute() instead
+ *
+ * @param string $component_name Component name
+ * @param string|null $config_key Specific configuration key (null for all)
+ * @param string $table_type Table type (components or defaults)
+ * @return mixed Configuration value(s) or null
*/
public function get_config($component_name, $config_key = null, $table_type = 'components') {
- global $wpdb;
- $table_name = $this->get_table_name($table_type);
+ _deprecated_function(
+ __FUNCTION__,
+ '2.0.0',
+ 'ROITheme\Application\UseCases\GetComponent\GetComponentUseCase::execute()'
+ );
- if ($config_key !== null) {
- // Obtener un valor específico
- $row = $wpdb->get_row($wpdb->prepare(
- "SELECT config_value, data_type FROM $table_name
- WHERE component_name = %s AND config_key = %s",
- $component_name,
- $config_key
- ));
+ $this->logDeprecation(__CLASS__, __FUNCTION__, func_get_args());
- if ($row) {
- return $this->parse_value($row->config_value, $row->data_type);
- }
- return null;
- }
-
- // Obtener toda la configuración del componente
- $rows = $wpdb->get_results($wpdb->prepare(
- "SELECT config_key, config_value, data_type FROM $table_name
- WHERE component_name = %s",
- $component_name
- ));
-
- $config = array();
- foreach ($rows as $row) {
- $config[$row->config_key] = $this->parse_value($row->config_value, $row->data_type);
- }
-
- return $config;
+ return $this->adapter->get_config($component_name, $config_key, $table_type);
}
/**
- * Parsear valor según tipo de dato
+ * Delete component configuration
*
- * @param string $value Valor almacenado
- * @param string $data_type Tipo de dato
- * @return mixed Valor parseado
- */
- private function parse_value($value, $data_type) {
- switch ($data_type) {
- case 'boolean':
- return (bool) $value;
- case 'integer':
- return (int) $value;
- case 'json':
- return json_decode($value, true);
- default:
- return $value;
- }
- }
-
- /**
- * Eliminar configuraciones de un componente
+ * @deprecated 2.0.0 Use DeleteComponentUseCase::execute() instead
*
- * @param string $component_name Nombre del componente
- * @param string $config_key Clave específica (opcional)
- * @param string $table_type Tipo de tabla: 'components' (personalizaciones) o 'defaults' (valores por defecto)
- * @return bool Éxito de la operación
+ * @param string $component_name Component name
+ * @param string|null $config_key Specific configuration key (null for all component)
+ * @param string $table_type Table type (components or defaults)
+ * @return bool Success status
*/
public function delete_config($component_name, $config_key = null, $table_type = 'components') {
- global $wpdb;
- $table_name = $this->get_table_name($table_type);
+ _deprecated_function(
+ __FUNCTION__,
+ '2.0.0',
+ 'ROITheme\Application\UseCases\DeleteComponent\DeleteComponentUseCase::execute()'
+ );
- if ($config_key !== null) {
- return $wpdb->delete(
- $table_name,
- array(
- 'component_name' => $component_name,
- 'config_key' => $config_key
- ),
- array('%s', '%s')
- ) !== false;
- }
+ $this->logDeprecation(__CLASS__, __FUNCTION__, func_get_args());
- // Eliminar todas las configuraciones del componente
- return $wpdb->delete(
- $table_name,
- array('component_name' => $component_name),
- array('%s')
- ) !== false;
+ // If deleting specific key, not supported in new architecture
+ // Delete entire component instead
+ return $this->adapter->delete_config($component_name, $table_type);
}
/**
- * Listar todos los componentes con configuraciones
+ * List all components
*
- * @param string $table_type Tipo de tabla: 'components' (personalizaciones) o 'defaults' (valores por defecto)
- * @return array Lista de nombres de componentes
+ * @deprecated 2.0.0 Use ComponentRepository::findAll() instead
+ *
+ * @param string $table_type Table type
+ * @return array List of components
*/
public function list_components($table_type = 'components') {
- global $wpdb;
- $table_name = $this->get_table_name($table_type);
+ _deprecated_function(
+ __FUNCTION__,
+ '2.0.0',
+ 'ROITheme\Domain\Contracts\ComponentRepositoryInterface::findAll()'
+ );
- return $wpdb->get_col(
- "SELECT DISTINCT component_name FROM $table_name ORDER BY component_name"
+ $this->logDeprecation(__CLASS__, __FUNCTION__, func_get_args());
+
+ global $wpdb;
+ $table = $this->get_table_name($table_type);
+
+ $query = "SELECT DISTINCT component_name FROM $table ORDER BY component_name ASC";
+ $components = $wpdb->get_col($query);
+
+ return $components ?: array();
+ }
+
+ /**
+ * Get legacy adapter
+ *
+ * @return \ROITheme\Infrastructure\Adapters\LegacyDBManagerAdapter
+ */
+ private function getLegacyAdapter() {
+ if (!class_exists('ROITheme\Infrastructure\Adapters\LegacyDBManagerAdapter')) {
+ require_once get_template_directory() . '/vendor/autoload.php';
+ }
+
+ return new \ROITheme\Infrastructure\Adapters\LegacyDBManagerAdapter();
+ }
+
+ /**
+ * Log deprecation usage
+ *
+ * @param string $class Class name
+ * @param string $method Method name
+ * @param array $args Arguments
+ */
+ private function logDeprecation($class, $method, $args = array()) {
+ if (!class_exists('ROITheme\Infrastructure\Logging\DeprecationLogger')) {
+ require_once get_template_directory() . '/vendor/autoload.php';
+ }
+
+ $logger = \ROITheme\Infrastructure\Logging\DeprecationLogger::getInstance();
+ $logger->log(
+ $class,
+ $method,
+ $args,
+ 'See documentation for Clean Architecture migration',
+ '2.0.0'
);
}
}
diff --git a/composer.json b/composer.json
deleted file mode 100644
index 1dacfa5a..00000000
--- a/composer.json
+++ /dev/null
@@ -1,39 +0,0 @@
-{
- "name": "roi/roi-theme",
- "description": "ROI Theme - Migración a Clean Architecture + POO",
- "type": "wordpress-theme",
- "license": "proprietary",
- "require": {
- "php": ">=8.0"
- },
- "require-dev": {
- "phpunit/phpunit": "^9.0",
- "squizlabs/php_codesniffer": "^3.7",
- "wp-coding-standards/wpcs": "^3.0",
- "mockery/mockery": "^1.5"
- },
- "autoload": {
- "psr-4": {
- "ROITheme\\": "src/"
- }
- },
- "autoload-dev": {
- "psr-4": {
- "ROITheme\\Tests\\": "tests/"
- }
- },
- "scripts": {
- "test": "phpunit",
- "phpcs": "phpcs --standard=WordPress src/",
- "phpcbf": "phpcbf --standard=WordPress src/"
- },
- "config": {
- "secure-http": true,
- "disable-tls": false,
- "preferred-install": "dist",
- "platform-check": false,
- "allow-plugins": {
- "dealerdirect/phpcodesniffer-composer-installer": true
- }
- }
-}
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
deleted file mode 100644
index d8213c9b..00000000
--- a/docs/ARCHITECTURE.md
+++ /dev/null
@@ -1,101 +0,0 @@
-# Arquitectura del Sistema - ROI Theme
-
-## Diagrama de Capas
-
-```
-┌─────────────────────────────────────────────────────────┐
-│ FRAMEWORKS & DRIVERS │
-│ (WordPress, MySQL) │
-└─────────────────────────────────────────────────────────┘
- ▲
- │
-┌─────────────────────────────────────────────────────────┐
-│ INFRASTRUCTURE LAYER │
-│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
-│ │ Repositories │ │ Controllers │ │ Services │ │
-│ │ (WordPress) │ │ (AJAX) │ │ (Validation) │ │
-│ └──────────────┘ └──────────────┘ └──────────────┘ │
-└─────────────────────────────────────────────────────────┘
- ▲
- │
-┌─────────────────────────────────────────────────────────┐
-│ APPLICATION LAYER │
-│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
-│ │ Use Cases │ │ DTOs │ │ Interfaces │ │
-│ │ (SaveComp) │ │ (Request) │ │ (Contracts) │ │
-│ └──────────────┘ └──────────────┘ └──────────────┘ │
-└─────────────────────────────────────────────────────────┘
- ▲
- │
-┌─────────────────────────────────────────────────────────┐
-│ DOMAIN LAYER │
-│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
-│ │ Entities │ │Value Objects │ │ Interfaces │ │
-│ │ (Component) │ │(CompName) │ │ (Repository) │ │
-│ └──────────────┘ └──────────────┘ └──────────────┘ │
-└─────────────────────────────────────────────────────────┘
-```
-
-## Reglas de Dependencia
-
-La **Regla de Dependencia** dice:
-
-> Las dependencias de código fuente deben apuntar **hacia adentro**, hacia políticas de más alto nivel.
-
-- **Dominio** no depende de nadie
-- **Aplicación** depende solo de Dominio
-- **Infraestructura** depende de Aplicación y Dominio
-- **Frameworks** son un detalle de Infraestructura
-
-## Capas Explicadas
-
-### 1. Domain Layer (Capa de Dominio)
-
-**Responsabilidad**: Lógica de negocio pura
-
-**Contiene**:
-- Entidades (Component)
-- Value Objects (ComponentName, ComponentConfiguration)
-- Interfaces de repositorios
-- Excepciones de dominio
-
-**NO contiene**:
-- Código de WordPress
-- Acceso a base de datos
-- Código de UI
-
-### 2. Application Layer (Capa de Aplicación)
-
-**Responsabilidad**: Casos de uso y orquestación
-
-**Contiene**:
-- Use Cases (SaveComponent, GetComponent)
-- DTOs (Request/Response objects)
-- Interfaces de servicios
-
-**NO contiene**:
-- Lógica de negocio (eso va en Dominio)
-- Detalles de implementación (eso va en Infraestructura)
-
-### 3. Infrastructure Layer (Capa de Infraestructura)
-
-**Responsabilidad**: Implementación de detalles técnicos
-
-**Contiene**:
-- Repositorios que usan WordPress/MySQL
-- Controllers (AJAX, REST)
-- Servicios (Validation, Cache)
-- Adaptadores de frameworks
-
-**Depende de**:
-- Application Layer (usa Use Cases)
-- Domain Layer (implementa interfaces)
-- WordPress
-
-## Ventajas de esta Arquitectura
-
-1. **Testeable**: Fácil de testear sin WordPress
-2. **Mantenible**: Separación clara de responsabilidades
-3. **Escalable**: Fácil agregar nuevos features
-4. **Independiente**: El dominio no depende de frameworks
-5. **Flexible**: Fácil cambiar implementaciones
diff --git a/docs/FASE-1-COMPLETADO.md b/docs/FASE-1-COMPLETADO.md
deleted file mode 100644
index 924d4e53..00000000
--- a/docs/FASE-1-COMPLETADO.md
+++ /dev/null
@@ -1,264 +0,0 @@
-# Fase 1: Estructura Base y DI Container - COMPLETADO ✓
-
-**Fecha de completitud**: 2025-01-17
-**Duración**: Según plan (5 días estimados)
-**Validación**: 48/48 checks pasados (100%)
-
----
-
-## Resumen Ejecutivo
-
-La Fase 1 de la migración a Clean Architecture + POO se ha completado exitosamente con **100% de validaciones pasadas**. Se ha establecido la estructura base completa del proyecto siguiendo los principios de Clean Architecture y Domain-Driven Design.
-
----
-
-## Tareas Completadas
-
-### 1.1 ✓ Estructura Completa de Carpetas Clean Architecture
-
-Creadas **28 carpetas** siguiendo la arquitectura de 4 capas:
-
-#### Domain Layer
-- `src/Domain/Component/` - Entidad principal
-- `src/Domain/Component/ValueObjects/` - Value Objects
-- `src/Domain/Component/Exceptions/` - Excepciones de dominio
-- `src/Domain/Shared/ValueObjects/` - Value Objects compartidos
-
-#### Application Layer
-- `src/Application/UseCases/SaveComponent/` - Caso de uso: Guardar componente
-- `src/Application/UseCases/GetComponent/` - Caso de uso: Obtener componente
-- `src/Application/UseCases/DeleteComponent/` - Caso de uso: Eliminar componente
-- `src/Application/UseCases/SyncSchema/` - Caso de uso: Sincronizar esquema
-- `src/Application/DTO/` - Data Transfer Objects
-- `src/Application/Contracts/` - Interfaces de servicios
-
-#### Infrastructure Layer
-- `src/Infrastructure/Persistence/WordPress/Repositories/` - Repositorios
-- `src/Infrastructure/API/WordPress/` - Controllers AJAX/REST
-- `src/Infrastructure/Services/` - Servicios de infraestructura
-- `src/Infrastructure/DI/` - Dependency Injection Container
-- `src/Infrastructure/Facades/` - Facades (patrón)
-- `src/Infrastructure/Presentation/Public/Renderers/` - Renderers públicos
-- `src/Infrastructure/Presentation/Admin/FormBuilders/` - Form builders admin
-- `src/Infrastructure/UI/Assets/` - Assets CSS/JS
-- `src/Infrastructure/UI/Views/` - Vistas/Templates
-
-#### Test Structure
-- `tests/Unit/Domain/Component/` - Tests unitarios de dominio
-- `tests/Unit/Application/UseCases/` - Tests de casos de uso
-- `tests/Unit/Infrastructure/Persistence/` - Tests de persistencia
-- `tests/Unit/Infrastructure/Services/` - Tests de servicios
-- `tests/Integration/` - Tests de integración
-- `tests/E2E/` - Tests end-to-end
-
-#### Otros
-- `schemas/` - Esquemas de base de datos
-- `templates/admin/` - Templates de administración
-- `templates/public/` - Templates públicos
-
----
-
-### 1.2 ✓ Composer con PSR-4 Autoloading
-
-**Archivo**: `composer.json`
-
-```json
-{
- "autoload": {
- "psr-4": {
- "ROITheme\\": "src/"
- }
- },
- "autoload-dev": {
- "psr-4": {
- "ROITheme\\Tests\\": "tests/"
- }
- }
-}
-```
-
-**Estado**:
-- ✓ Autoloader optimizado generado
-- ✓ 1147 clases cargadas
-- ✓ Funcionamiento verificado
-
----
-
-### 1.3 ✓ DI Container Implementado
-
-**Archivo**: `src/Infrastructure/DI/DIContainer.php`
-
-**Características**:
-- ✓ Patrón Singleton implementado
-- ✓ Prevención de clonación (`__clone()` privado)
-- ✓ Prevención de deserialización (`__wakeup()` lanza excepción)
-- ✓ Registro de servicios con `set()`
-- ✓ Recuperación de servicios con `get()`
-- ✓ Verificación de existencia con `has()`
-- ✓ Getters específicos:
- - `getComponentRepository()`
- - `getValidationService()`
- - `getCacheService()`
-- ✓ Método `reset()` para testing
-
-**Interfaces Creadas**:
-1. `src/Domain/Component/ComponentRepositoryInterface.php`
-2. `src/Application/Contracts/ValidationServiceInterface.php`
-3. `src/Application/Contracts/CacheServiceInterface.php`
-
-**Entidades Creadas**:
-1. `src/Domain/Component/Component.php` (placeholder)
-
----
-
-### 1.4 ✓ Bootstrap en functions.php
-
-**Archivo**: `functions.php` (líneas 14-46)
-
-**Implementado**:
-```php
-// Load Composer autoloader
-if (file_exists(__DIR__ . '/vendor/autoload.php')) {
- require_once __DIR__ . '/vendor/autoload.php';
-}
-
-// Initialize DI Container
-use ROITheme\Infrastructure\DI\DIContainer;
-
-/**
- * Helper function to access DI Container
- */
-function roi_container(): DIContainer {
- return DIContainer::getInstance();
-}
-```
-
-**Estado**: ✓ Funcionando correctamente
-
----
-
-### 1.5 ✓ Tests Unitarios
-
-**Archivo**: `tests/Unit/Infrastructure/DI/DIContainerTest.php`
-
-**Tests Implementados**: 10 tests, 24 assertions
-
-1. ✓ `it_should_return_singleton_instance()` - Verifica patrón Singleton
-2. ✓ `it_should_prevent_cloning()` - Prevención de clonación
-3. ✓ `it_should_prevent_unserialization()` - Prevención de deserialización
-4. ✓ `it_should_register_and_retrieve_service()` - Registro/recuperación
-5. ✓ `it_should_return_null_for_non_existent_service()` - Servicio inexistente
-6. ✓ `it_should_throw_exception_for_unimplemented_component_repository()` - Placeholder
-7. ✓ `it_should_throw_exception_for_unimplemented_validation_service()` - Placeholder
-8. ✓ `it_should_throw_exception_for_unimplemented_cache_service()` - Placeholder
-9. ✓ `it_should_reset_singleton_instance()` - Reset para testing
-10. ✓ `it_should_manage_multiple_services()` - Múltiples servicios
-
-**Resultado Total**:
-- Tests: 13 (10 DIContainer + 3 Example)
-- Assertions: 28
-- Warnings: 1 (deprecation notice PHPUnit 10)
-- **Estado**: TODOS PASANDO ✓
-
----
-
-### 1.6 ✓ Validación Final
-
-**Script**: `scripts/validate-phase-1.php`
-
-**Resultado**: **48/48 validaciones pasadas (100%)**
-
-**Categorías Validadas**:
-1. ✓ Estructura de carpetas (28 checks)
-2. ✓ Composer y autoloader (3 checks)
-3. ✓ DI Container (6 checks)
-4. ✓ Interfaces (4 checks)
-5. ✓ Bootstrap (4 checks)
-6. ✓ Tests unitarios (3 checks)
-
----
-
-## Git
-
-**Branch**: `migration/clean-architecture`
-**Commit**: `de5ff` - Fase 1: Estructura Base y DI Container - Clean Architecture
-**Tag**: `v1.0.0`
-
-**Estadísticas del Commit**:
-- 149 archivos modificados
-- 3,187 inserciones (+)
-- 9,554 eliminaciones (-)
-- Limpieza de archivos legacy y documentación obsoleta
-
----
-
-## Archivos Clave Creados
-
-### Código de Producción
-1. `src/Infrastructure/DI/DIContainer.php` - DI Container (Singleton)
-2. `src/Domain/Component/Component.php` - Entidad Component (placeholder)
-3. `src/Domain/Component/ComponentRepositoryInterface.php` - Interfaz de repositorio
-4. `src/Application/Contracts/ValidationServiceInterface.php` - Interfaz de validación
-5. `src/Application/Contracts/CacheServiceInterface.php` - Interfaz de cache
-
-### Tests
-6. `tests/Unit/Infrastructure/DI/DIContainerTest.php` - Tests del DI Container
-
-### Scripts y Documentación
-7. `scripts/validate-phase-1.php` - Script de validación automatizado
-8. `docs/ARCHITECTURE.md` - Documentación de arquitectura
-9. `docs/GIT-BRANCHING-STRATEGY.md` - Estrategia de branching
-10. `src/Domain/README.md` - Documentación capa Domain
-11. `src/Application/README.md` - Documentación capa Application
-12. `src/Infrastructure/README.md` - Documentación capa Infrastructure
-
----
-
-## Próximos Pasos
-
-La arquitectura base está lista para la **Fase 2: Entidades y Value Objects**.
-
-**Prerequisitos cumplidos para Fase 2**:
-- ✓ Estructura de carpetas completa
-- ✓ Autoloading PSR-4 funcionando
-- ✓ DI Container implementado y testeado
-- ✓ Bootstrap inicializando la arquitectura
-- ✓ Suite de tests configurada y pasando
-
-**Fase 2 incluirá**:
-- Implementación completa de la entidad `Component`
-- Value Objects: `ComponentName`, `ComponentConfiguration`, etc.
-- Excepciones de dominio
-- Reglas de negocio puras
-- Tests unitarios de dominio
-
----
-
-## Validación de Calidad
-
-- ✓ Código siguiendo PSR-4
-- ✓ Type hints estrictos (`declare(strict_types=1)`)
-- ✓ DocBlocks completos
-- ✓ Tests con 100% de cobertura del DI Container
-- ✓ Zero dependencias de WordPress en Domain/Application
-- ✓ Dependency Inversion Principle aplicado
-- ✓ Single Responsibility Principle aplicado
-
----
-
-## Notas Técnicas
-
-1. **Placeholders**: Los servicios `ComponentRepository`, `ValidationService` y `CacheService` lanzarán `RuntimeException` hasta que sean implementados en Fase 5.
-
-2. **Tests Warning**: Hay un warning de deprecación en PHPUnit sobre `expectError()`. Esto se resolverá cuando migremos a PHPUnit 10 en el futuro.
-
-3. **Windows Compatibility**: El script de validación está optimizado para Windows con manejo especial de rutas (`DIRECTORY_SEPARATOR`).
-
-4. **Autoloader Optimizado**: Se usa `composer dump-autoload -o` para generar autoloader optimizado con class map.
-
----
-
-**Estado General**: ✅ FASE 1 COMPLETADA EXITOSAMENTE
-
-**Validado por**: Script automatizado `scripts/validate-phase-1.php`
-**Fecha**: 2025-01-17
diff --git a/docs/GIT-BRANCHING-STRATEGY.md b/docs/GIT-BRANCHING-STRATEGY.md
deleted file mode 100644
index 331f2705..00000000
--- a/docs/GIT-BRANCHING-STRATEGY.md
+++ /dev/null
@@ -1,103 +0,0 @@
-# Estrategia de Git Branching - ROI Theme Migration
-
-## Modelo de Branching
-
-Usaremos un modelo simplificado basado en **Git Flow** adaptado para esta migración.
-
-## Estructura de Ramas
-
-```
-main (producción)
-│
-└── migration/clean-architecture (rama principal de migración)
- │
- ├── migration/fase-1-infrastructure
- ├── migration/fase-2-database
- ├── migration/fase-3-domain
- ├── migration/fase-4-application
- ├── migration/fase-5-infrastructure-layer
- ├── migration/fase-6-components
- ├── migration/fase-7-testing
- ├── migration/fase-8-deprecation
- └── migration/fase-9-documentation
-```
-
-## Reglas de Trabajo
-
-### 1. Rama Principal de Migración
-
-- **Nombre**: `migration/clean-architecture`
-- **Propósito**: Integrar todas las fases de la migración
-- **Protegida**: NO se pushea directamente a esta rama
-- **Base para**: Todas las ramas de fases individuales
-
-### 2. Ramas de Fases
-
-- **Naming**: `migration/fase-{número}-{nombre-descriptivo}`
-- **Flujo**:
- 1. Crear desde `migration/clean-architecture`
- 2. Desarrollar la fase completa
- 3. Merge de vuelta a `migration/clean-architecture`
- 4. Tag de versión (v{fase}.0.0)
-
-## Workflow por Fase
-
-```bash
-# 1. Asegurarse de estar en rama principal de migración
-git checkout migration/clean-architecture
-git pull origin migration/clean-architecture
-
-# 2. Crear rama para nueva fase
-git checkout -b migration/fase-1-infrastructure
-
-# 3. Trabajar en la fase
-git add .
-git commit -m "Descripción del cambio"
-
-# 4. Al completar la fase
-git checkout migration/clean-architecture
-git merge --no-ff migration/fase-1-infrastructure -m "Merge Fase 1: Infrastructure Base"
-
-# 5. Crear tag de versión
-git tag -a v1.0.0 -m "Fase 1 completada: Infrastructure Base"
-
-# 6. Push (si hay repositorio remoto)
-git push origin migration/clean-architecture
-git push origin v1.0.0
-```
-
-## Commits
-
-### Formato de Mensajes
-
-```
-Fase {número}: {Título corto}
-
-- Cambio 1
-- Cambio 2
-- Cambio 3
-```
-
-## Tags
-
-### Convención de Versionado
-
-- **Fase completada**: `v{fase}.0.0`
- - Ejemplo: `v1.0.0` (Fase 1 completada)
-- **Correcciones menores**: `v{fase}.{minor}.0`
-- **Hotfixes**: `v{fase}.{minor}.{patch}`
-
-## Checklist Pre-Commit
-
-- [ ] Código sigue estándares (`composer phpcs`)
-- [ ] Tests pasan (`composer test`)
-- [ ] No hay archivos de backup incluidos
-- [ ] No hay credenciales hardcodeadas
-- [ ] Mensaje de commit es descriptivo
-
-## Checklist Pre-Merge
-
-- [ ] Todos los tests de la fase pasan
-- [ ] Código sigue estándares PHPCS
-- [ ] Documentación actualizada
-- [ ] Tag de versión creado
diff --git a/functions.php b/functions.php
index 992a42d4..279cfa0b 100644
--- a/functions.php
+++ b/functions.php
@@ -45,6 +45,69 @@ function roi_container(): DIContainer {
* ========================================================================
*/
+/**
+ * ========================================================================
+ * THEME DATABASE TABLES SETUP
+ * ========================================================================
+ *
+ * Crea las tablas del tema cuando se activa.
+ * Esto asegura que el tema sea portable y funcione en cualquier instalación WordPress.
+ */
+
+/**
+ * Crear tablas del tema en la activación
+ *
+ * Este hook se ejecuta cuando el tema se activa en WordPress.
+ * Crea las tablas necesarias si no existen.
+ *
+ * @since 1.0.19
+ */
+add_action('after_switch_theme', function() {
+ global $wpdb;
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
+
+ $charset_collate = $wpdb->get_charset_collate();
+
+ // Tabla de components
+ $table_components = $wpdb->prefix . 'roi_theme_components';
+ $sql_components = "CREATE TABLE {$table_components} (
+ id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
+ component_name VARCHAR(50) NOT NULL,
+ configuration LONGTEXT NOT NULL,
+ content LONGTEXT,
+ visibility TEXT NOT NULL,
+ is_enabled TINYINT(1) NOT NULL DEFAULT 1,
+ schema_version VARCHAR(20) NOT NULL,
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ PRIMARY KEY (id),
+ UNIQUE KEY component_name (component_name),
+ INDEX idx_enabled (is_enabled),
+ INDEX idx_schema_version (schema_version)
+ ) {$charset_collate};";
+
+ // Tabla de defaults/schemas
+ $table_defaults = $wpdb->prefix . 'roi_theme_defaults';
+ $sql_defaults = "CREATE TABLE {$table_defaults} (
+ id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
+ component_name VARCHAR(50) NOT NULL,
+ default_schema LONGTEXT NOT NULL,
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ PRIMARY KEY (id),
+ UNIQUE KEY component_name (component_name)
+ ) {$charset_collate};";
+
+ // Crear/actualizar tablas
+ dbDelta($sql_components);
+ dbDelta($sql_defaults);
+
+ // Log en modo debug
+ if (defined('WP_DEBUG') && WP_DEBUG) {
+ error_log('ROI Theme: Database tables created/updated');
+ }
+});
+
/**
* Theme Version
*/
diff --git a/phpcs-baseline.txt b/phpcs-baseline.txt
deleted file mode 100644
index c38270bb..00000000
--- a/phpcs-baseline.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-Time: 132ms; Memory: 10MB
-
diff --git a/phpcs.xml b/phpcs.xml
deleted file mode 100644
index 429ccdca..00000000
--- a/phpcs.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
- Estándares de código para ROI Theme
-
-
- src
-
-
- */vendor/*
- */tests/*
- */admin/*
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/phpunit.xml b/phpunit.xml
deleted file mode 100644
index 8aaf2c2d..00000000
--- a/phpunit.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
-
- tests/Unit
-
-
- tests/Integration
-
-
- tests/E2E
-
-
-
-
-
- src
-
-
- src/Infrastructure/UI/Views
-
-
-
-
-
-
-
diff --git a/scripts/validate-phase-1.php b/scripts/validate-phase-1.php
deleted file mode 100644
index 42a7ebad..00000000
--- a/scripts/validate-phase-1.php
+++ /dev/null
@@ -1,293 +0,0 @@
-checkFolderStructure();
- $this->checkComposerAutoloader();
- $this->checkDIContainer();
- $this->checkInterfaces();
- $this->checkBootstrap();
- $this->checkTests();
-
- $this->printResults();
- }
-
- private function check(string $description, callable $test): void
- {
- $this->totalChecks++;
-
- try {
- $result = $test();
-
- if ($result) {
- echo "✓ {$description}\n";
- $this->passedChecks++;
- } else {
- echo "✗ {$description}\n";
- $this->errors[] = $description;
- }
- } catch (Exception $e) {
- echo "✗ {$description} (Error: {$e->getMessage()})\n";
- $this->errors[] = $description . ': ' . $e->getMessage();
- }
- }
-
- private function checkFolderStructure(): void
- {
- echo "Validando estructura de carpetas...\n";
-
- $requiredDirs = [
- 'src/Domain/Component',
- 'src/Domain/Component/ValueObjects',
- 'src/Domain/Component/Exceptions',
- 'src/Domain/Shared/ValueObjects',
- 'src/Application/UseCases/SaveComponent',
- 'src/Application/UseCases/GetComponent',
- 'src/Application/UseCases/DeleteComponent',
- 'src/Application/UseCases/SyncSchema',
- 'src/Application/DTO',
- 'src/Application/Contracts',
- 'src/Infrastructure/Persistence/WordPress/Repositories',
- 'src/Infrastructure/API/WordPress',
- 'src/Infrastructure/Services',
- 'src/Infrastructure/DI',
- 'src/Infrastructure/Facades',
- 'src/Infrastructure/Presentation/Public/Renderers',
- 'src/Infrastructure/Presentation/Admin/FormBuilders',
- 'src/Infrastructure/UI/Assets',
- 'src/Infrastructure/UI/Views',
- 'tests/Unit/Domain/Component',
- 'tests/Unit/Application/UseCases',
- 'tests/Unit/Infrastructure/Persistence',
- 'tests/Unit/Infrastructure/Services',
- 'tests/Integration',
- 'tests/E2E',
- 'schemas',
- 'templates/admin',
- 'templates/public',
- ];
-
- foreach ($requiredDirs as $dir) {
- $this->check(
- "Carpeta {$dir} existe",
- fn() => is_dir(__DIR__ . '/../' . $dir)
- );
- }
-
- echo "\n";
- }
-
- private function checkComposerAutoloader(): void
- {
- echo "Validando Composer y autoloader...\n";
-
- $this->check(
- 'composer.json existe',
- fn() => file_exists(__DIR__ . '/../composer.json')
- );
-
- $this->check(
- 'vendor/autoload.php existe',
- fn() => file_exists(__DIR__ . '/../vendor/autoload.php')
- );
-
- $this->check(
- 'PSR-4 autoloading configurado',
- function () {
- $composer = json_decode(file_get_contents(__DIR__ . '/../composer.json'), true);
- return isset($composer['autoload']['psr-4']['ROITheme\\']);
- }
- );
-
- echo "\n";
- }
-
- private function checkDIContainer(): void
- {
- echo "Validando DI Container...\n";
-
- require_once __DIR__ . '/../vendor/autoload.php';
-
- $this->check(
- 'DIContainer class existe',
- fn() => class_exists('ROITheme\Infrastructure\DI\DIContainer')
- );
-
- $this->check(
- 'DIContainer es singleton',
- function () {
- $instance1 = \ROITheme\Infrastructure\DI\DIContainer::getInstance();
- $instance2 = \ROITheme\Infrastructure\DI\DIContainer::getInstance();
- return $instance1 === $instance2;
- }
- );
-
- $this->check(
- 'DIContainer tiene método set()',
- fn() => method_exists('ROITheme\Infrastructure\DI\DIContainer', 'set')
- );
-
- $this->check(
- 'DIContainer tiene método get()',
- fn() => method_exists('ROITheme\Infrastructure\DI\DIContainer', 'get')
- );
-
- $this->check(
- 'DIContainer tiene método has()',
- fn() => method_exists('ROITheme\Infrastructure\DI\DIContainer', 'has')
- );
-
- $this->check(
- 'DIContainer tiene métodos de servicio',
- function () {
- return method_exists('ROITheme\Infrastructure\DI\DIContainer', 'getComponentRepository')
- && method_exists('ROITheme\Infrastructure\DI\DIContainer', 'getValidationService')
- && method_exists('ROITheme\Infrastructure\DI\DIContainer', 'getCacheService');
- }
- );
-
- echo "\n";
- }
-
- private function checkInterfaces(): void
- {
- echo "Validando interfaces...\n";
-
- $this->check(
- 'ComponentRepositoryInterface existe',
- fn() => interface_exists('ROITheme\Domain\Component\ComponentRepositoryInterface')
- );
-
- $this->check(
- 'ValidationServiceInterface existe',
- fn() => interface_exists('ROITheme\Application\Contracts\ValidationServiceInterface')
- );
-
- $this->check(
- 'CacheServiceInterface existe',
- fn() => interface_exists('ROITheme\Application\Contracts\CacheServiceInterface')
- );
-
- $this->check(
- 'Component entity existe',
- fn() => class_exists('ROITheme\Domain\Component\Component')
- );
-
- echo "\n";
- }
-
- private function checkBootstrap(): void
- {
- echo "Validando bootstrap en functions.php...\n";
-
- $functionsContent = file_get_contents(__DIR__ . '/../functions.php');
-
- $this->check(
- 'functions.php existe',
- fn() => file_exists(__DIR__ . '/../functions.php')
- );
-
- $this->check(
- 'functions.php carga Composer autoloader',
- fn() => strpos($functionsContent, "require_once __DIR__ . '/vendor/autoload.php'") !== false
- );
-
- $this->check(
- 'functions.php importa DIContainer',
- fn() => strpos($functionsContent, 'use ROITheme\Infrastructure\DI\DIContainer') !== false
- );
-
- $this->check(
- 'functions.php define roi_container()',
- fn() => strpos($functionsContent, 'function roi_container()') !== false
- );
-
- echo "\n";
- }
-
- private function checkTests(): void
- {
- echo "Validando tests unitarios...\n";
-
- $this->check(
- 'phpunit.xml configurado',
- fn() => file_exists(__DIR__ . '/../phpunit.xml')
- );
-
- $this->check(
- 'DIContainerTest existe',
- fn() => file_exists(__DIR__ . '/../tests/Unit/Infrastructure/DI/DIContainerTest.php')
- );
-
- // Run PHPUnit tests
- $baseDir = dirname(__DIR__);
- $phpunitPath = $baseDir . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'phpunit';
-
- // For Windows compatibility
- if (PHP_OS_FAMILY === 'Windows') {
- $command = "cd /d \"$baseDir\" && \"$phpunitPath\" --testsuite Unit 2>&1";
- } else {
- $command = "cd \"$baseDir\" && \"$phpunitPath\" --testsuite Unit 2>&1";
- }
-
- exec($command, $output, $returnCode);
-
- $this->check(
- 'Tests unitarios pasan',
- fn() => $returnCode === 0
- );
-
- echo "\n";
- }
-
- private function printResults(): void
- {
- echo "========================================\n";
- echo " RESULTADOS DE VALIDACIÓN\n";
- echo "========================================\n\n";
-
- $percentage = $this->totalChecks > 0
- ? round(($this->passedChecks / $this->totalChecks) * 100, 2)
- : 0;
-
- echo "Checks pasados: {$this->passedChecks}/{$this->totalChecks} ({$percentage}%)\n\n";
-
- if (count($this->errors) > 0) {
- echo "ERRORES ENCONTRADOS:\n";
- foreach ($this->errors as $error) {
- echo " - {$error}\n";
- }
- echo "\n";
- exit(1);
- } else {
- echo "✓ ¡FASE 1 COMPLETADA EXITOSAMENTE!\n\n";
- echo "Todas las validaciones pasaron correctamente.\n";
- echo "La arquitectura base está lista para la Fase 2.\n\n";
- exit(0);
- }
- }
-}
-
-// Run validation
-$validator = new Phase1Validator();
-$validator->run();
diff --git a/src/Application/Contracts/CacheServiceInterface.php b/src/Application/Contracts/CacheServiceInterface.php
deleted file mode 100644
index f7daad26..00000000
--- a/src/Application/Contracts/CacheServiceInterface.php
+++ /dev/null
@@ -1,49 +0,0 @@
- $data Data to validate
- * @param array $rules Validation rules
- * @return bool
- */
- public function validate(array $data, array $rules): bool;
-
- /**
- * Get validation errors
- *
- * @return array
- */
- public function getErrors(): array;
-}
diff --git a/src/Application/DTO/.gitkeep b/src/Application/DTO/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/Application/README.md b/src/Application/README.md
deleted file mode 100644
index e5824448..00000000
--- a/src/Application/README.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# Capa de Aplicación
-
-## Propósito
-
-La capa de Aplicación **orquesta** la lógica de negocio del dominio. Contiene los **Use Cases** (casos de uso) que coordinan las operaciones entre el dominio y la infraestructura.
-
-## Principios
-
-1. **Orquestación**: Coordina flujos de trabajo usando entidades de dominio
-2. **DTOs**: Usa Data Transfer Objects para comunicación entre capas
-3. **Contratos**: Define interfaces que la infraestructura implementará
-4. **Sin lógica de negocio**: Delega al dominio
-
-## Estructura
-
-```
-Application/
-├── UseCases/ # Casos de uso
-│ ├── SaveComponent/
-│ │ ├── SaveComponentUseCase.php
-│ │ ├── SaveComponentRequest.php
-│ │ └── SaveComponentResponse.php
-│ ├── GetComponent/
-│ │ └── GetComponentUseCase.php
-│ └── DeleteComponent/
-│ └── DeleteComponentUseCase.php
-├── DTO/ # Data Transfer Objects
-│ ├── ComponentDTO.php
-│ └── ...
-└── Contracts/ # Interfaces de servicios
- ├── ValidationServiceInterface.php
- ├── CacheServiceInterface.php
- └── ...
-```
-
-## Reglas
-
-1. **SÍ** usar dependencias via constructor injection
-2. **SÍ** usar interfaces del dominio
-3. **SÍ** usar DTOs para entrada/salida
-4. **NO** contener lógica de negocio (eso va en Dominio)
-5. **NO** depender de frameworks (solo via interfaces)
diff --git a/src/Application/UseCases/SyncSchema/.gitkeep b/src/Application/UseCases/SyncSchema/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/Domain/Component/Component.php b/src/Domain/Component/Component.php
deleted file mode 100644
index 55e46a49..00000000
--- a/src/Domain/Component/Component.php
+++ /dev/null
@@ -1,64 +0,0 @@
- $configuration Component configuration
- */
- public function __construct(int $id, string $name, array $configuration = [])
- {
- $this->id = $id;
- $this->name = $name;
- $this->configuration = $configuration;
- }
-
- /**
- * Get component ID
- *
- * @return int
- */
- public function getId(): int
- {
- return $this->id;
- }
-
- /**
- * Get component name
- *
- * @return string
- */
- public function getName(): string
- {
- return $this->name;
- }
-
- /**
- * Get component configuration
- *
- * @return array
- */
- public function getConfiguration(): array
- {
- return $this->configuration;
- }
-}
diff --git a/src/Domain/Component/ComponentRepositoryInterface.php b/src/Domain/Component/ComponentRepositoryInterface.php
deleted file mode 100644
index 8a113997..00000000
--- a/src/Domain/Component/ComponentRepositoryInterface.php
+++ /dev/null
@@ -1,47 +0,0 @@
-
- */
- public function findAll(): array;
-
- /**
- * Delete a component
- *
- * @param int $id Component ID
- * @return bool
- */
- public function delete(int $id): bool;
-}
diff --git a/src/Domain/README.md b/src/Domain/README.md
deleted file mode 100644
index 6e9daff2..00000000
--- a/src/Domain/README.md
+++ /dev/null
@@ -1,43 +0,0 @@
-# Capa de Dominio
-
-## Propósito
-
-La capa de Dominio contiene la **lógica de negocio pura** del sistema. Es el corazón de la aplicación y no debe tener dependencias de frameworks, librerías externas, o capas superiores.
-
-## Principios
-
-1. **Independencia**: No depende de WordPress, base de datos, o cualquier framework
-2. **Inmutabilidad**: Los Value Objects son inmutables (readonly)
-3. **Validación**: Toda validación de reglas de negocio ocurre aquí
-4. **Pureza**: Funciones puras sin side effects
-
-## Estructura
-
-```
-Domain/
-├── Component/ # Entidad principal: Component
-│ ├── Component.php # Entidad Component
-│ ├── ComponentRepositoryInterface.php # Contrato para repositorio
-│ ├── ValueObjects/ # Value Objects del componente
-│ │ ├── ComponentName.php
-│ │ ├── ComponentConfiguration.php
-│ │ ├── ComponentVisibility.php
-│ │ └── ComponentContent.php
-│ └── Exceptions/ # Excepciones de dominio
-│ ├── InvalidComponentException.php
-│ └── ComponentNotFoundException.php
-└── Shared/ # Value Objects compartidos
- └── ValueObjects/
- ├── Email.php
- ├── Url.php
- └── ...
-```
-
-## Reglas
-
-1. **NO** usar código de WordPress (`global $wpdb`, `wp_*` functions)
-2. **NO** acceder a base de datos directamente
-3. **NO** usar `$_POST`, `$_GET`, `$_SESSION`
-4. **SÍ** definir interfaces para dependencias
-5. **SÍ** validar datos en constructores
-6. **SÍ** lanzar excepciones de dominio cuando algo viola reglas de negocio
diff --git a/src/Domain/Shared/.gitkeep b/src/Domain/Shared/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/Infrastructure/API/WordPress/MigrationCommand.php b/src/Infrastructure/API/WordPress/MigrationCommand.php
deleted file mode 100644
index c53fed7a..00000000
--- a/src/Infrastructure/API/WordPress/MigrationCommand.php
+++ /dev/null
@@ -1,189 +0,0 @@
-simulateMigration($migrator);
- return;
- }
-
- // Ejecutar migración real
- $start_time = microtime(true);
- $result = $migrator->migrate();
- $end_time = microtime(true);
- $duration = round($end_time - $start_time, 2);
-
- \WP_CLI::line('');
-
- if ($result['success']) {
- \WP_CLI::success('✅ ' . $result['message']);
- \WP_CLI::line('');
- \WP_CLI::line('📊 Estadísticas de Migración:');
- \WP_CLI::line(' ├─ Components migrados: ' . ($result['stats']['components']['migrated'] ?? 0));
- \WP_CLI::line(' ├─ Defaults migrados: ' . ($result['stats']['defaults']['migrated'] ?? 0));
- \WP_CLI::line(' ├─ Tiempo de ejecución: ' . $duration . 's');
- \WP_CLI::line(' └─ Validación: ' . $result['stats']['validation']['message']);
- \WP_CLI::line('');
- \WP_CLI::line('⚠️ IMPORTANTE: Tablas legacy respaldadas con sufijo _backup');
- \WP_CLI::line(' Validar funcionamiento por 7-30 días antes de limpiar backup.');
- \WP_CLI::line(' Comando para limpiar: wp roi-theme cleanup-backup');
- \WP_CLI::line('');
- } else {
- \WP_CLI::error('❌ ' . $result['message']);
- }
- }
-
- /**
- * Limpiar tablas de backup después de validar migración
- *
- * IMPORTANTE: Solo ejecutar después de validar que todo funciona correctamente
- *
- * ## EJEMPLOS
- *
- * wp roi-theme cleanup-backup
- *
- * @param array $args Argumentos posicionales
- * @param array $assoc_args Argumentos asociativos
- * @return void
- */
- public function cleanup_backup(array $args, array $assoc_args): void
- {
- global $wpdb;
-
- \WP_CLI::line('');
- \WP_CLI::confirm(
- '⚠️ ¿Estás seguro de eliminar las tablas de backup? Esta acción es IRREVERSIBLE.',
- $assoc_args
- );
-
- $migrator = new DatabaseMigrator($wpdb);
- $migrator->cleanupBackup();
-
- \WP_CLI::line('');
- \WP_CLI::success('✅ Tablas de backup eliminadas correctamente');
- \WP_CLI::line('');
- }
-
- /**
- * Simular migración sin hacer cambios reales
- *
- * @param DatabaseMigrator $migrator Instancia del migrador
- * @return void
- */
- private function simulateMigration(DatabaseMigrator $migrator): void
- {
- global $wpdb;
-
- \WP_CLI::line('Verificando tablas legacy...');
-
- // Verificar existencia de tablas
- $legacy_components = $wpdb->prefix . 'roi_theme_components';
- $legacy_defaults = $wpdb->prefix . 'roi_theme_components_defaults';
-
- $components_exist = $wpdb->get_var("SHOW TABLES LIKE '{$legacy_components}'") === $legacy_components;
- $defaults_exist = $wpdb->get_var("SHOW TABLES LIKE '{$legacy_defaults}'") === $legacy_defaults;
-
- if (!$components_exist || !$defaults_exist) {
- \WP_CLI::error('Tablas legacy no encontradas. No hay nada que migrar.');
- return;
- }
-
- \WP_CLI::line('✓ Tablas legacy encontradas');
-
- // Contar registros
- $components_count = $wpdb->get_var("SELECT COUNT(*) FROM {$legacy_components}");
- $defaults_count = $wpdb->get_var("SELECT COUNT(*) FROM {$legacy_defaults}");
-
- \WP_CLI::line('');
- \WP_CLI::line('📊 Datos a migrar:');
- \WP_CLI::line(" ├─ Components: {$components_count} registros");
- \WP_CLI::line(" └─ Defaults: {$defaults_count} registros");
- \WP_CLI::line('');
- \WP_CLI::line('Operaciones que se ejecutarían:');
- \WP_CLI::line(' 1. Crear tablas v2 con nueva estructura (config_group)');
- \WP_CLI::line(' 2. Migrar ' . ($components_count + $defaults_count) . ' registros');
- \WP_CLI::line(' 3. Validar integridad de datos');
- \WP_CLI::line(' 4. Renombrar tablas (legacy → _backup, v2 → producción)');
- \WP_CLI::line('');
-
- \WP_CLI::success('✅ Simulación completada. Ejecutar sin --dry-run para migración real.');
- \WP_CLI::line('');
- }
-}
-
-// Registrar comando WP-CLI
-if (defined('WP_CLI') && WP_CLI) {
- \WP_CLI::add_command('roi-theme', MigrationCommand::class);
-}
diff --git a/src/Infrastructure/DI/DIContainer.php b/src/Infrastructure/DI/DIContainer.php
deleted file mode 100644
index 30a9f8c4..00000000
--- a/src/Infrastructure/DI/DIContainer.php
+++ /dev/null
@@ -1,153 +0,0 @@
- */
- private array $services = [];
-
- /**
- * Private constructor to prevent direct instantiation
- */
- private function __construct()
- {
- // Initialize services here
- }
-
- /**
- * Get singleton instance
- *
- * @return self
- */
- public static function getInstance(): self
- {
- if (self::$instance === null) {
- self::$instance = new self();
- }
-
- return self::$instance;
- }
-
- /**
- * Prevent cloning
- */
- private function __clone()
- {
- }
-
- /**
- * Prevent unserialization
- *
- * @throws \Exception
- */
- public function __wakeup(): void
- {
- throw new \Exception('Cannot unserialize singleton');
- }
-
- /**
- * Get Component Repository
- *
- * @return ComponentRepositoryInterface
- */
- public function getComponentRepository(): ComponentRepositoryInterface
- {
- if (!isset($this->services['component_repository'])) {
- // Placeholder - will be replaced in Phase 5
- throw new \RuntimeException('ComponentRepository not implemented yet. Will be available in Phase 5.');
- }
-
- return $this->services['component_repository'];
- }
-
- /**
- * Get Validation Service
- *
- * @return ValidationServiceInterface
- */
- public function getValidationService(): ValidationServiceInterface
- {
- if (!isset($this->services['validation_service'])) {
- // Placeholder - will be replaced in Phase 5
- throw new \RuntimeException('ValidationService not implemented yet. Will be available in Phase 5.');
- }
-
- return $this->services['validation_service'];
- }
-
- /**
- * Get Cache Service
- *
- * @return CacheServiceInterface
- */
- public function getCacheService(): CacheServiceInterface
- {
- if (!isset($this->services['cache_service'])) {
- // Placeholder - will be replaced in Phase 5
- throw new \RuntimeException('CacheService not implemented yet. Will be available in Phase 5.');
- }
-
- return $this->services['cache_service'];
- }
-
- /**
- * Register a service
- *
- * @param string $key Service identifier
- * @param object $service Service instance
- * @return void
- */
- public function set(string $key, object $service): void
- {
- $this->services[$key] = $service;
- }
-
- /**
- * Check if service exists
- *
- * @param string $key Service identifier
- * @return bool
- */
- public function has(string $key): bool
- {
- return isset($this->services[$key]);
- }
-
- /**
- * Get a service by key
- *
- * @param string $key Service identifier
- * @return object|null
- */
- public function get(string $key): ?object
- {
- return $this->services[$key] ?? null;
- }
-
- /**
- * Reset container (useful for testing)
- *
- * @return void
- */
- public static function reset(): void
- {
- self::$instance = null;
- }
-}
diff --git a/src/Infrastructure/Facades/.gitkeep b/src/Infrastructure/Facades/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/Infrastructure/Persistence/WordPress/DatabaseMigrator.php b/src/Infrastructure/Persistence/WordPress/DatabaseMigrator.php
deleted file mode 100644
index c340eaab..00000000
--- a/src/Infrastructure/Persistence/WordPress/DatabaseMigrator.php
+++ /dev/null
@@ -1,515 +0,0 @@
-migrate();
- *
- * if ($result['success']) {
- * echo "Migración exitosa: " . $result['stats']['components']['migrated'] . " registros";
- * } else {
- * echo "Error: " . $result['message'];
- * }
- * ```
- */
-final class DatabaseMigrator
-{
- /**
- * @var \wpdb Instancia de WordPress Database
- */
- private \wpdb $wpdb;
-
- /**
- * @var string Charset y Collation de la BD
- */
- private string $charset_collate;
-
- /**
- * @var array Log de operaciones ejecutadas
- */
- private array $log = [];
-
- /**
- * Constructor
- *
- * @param \wpdb $wpdb Instancia de WordPress Database
- */
- public function __construct(\wpdb $wpdb)
- {
- $this->wpdb = $wpdb;
- $this->charset_collate = $wpdb->get_charset_collate();
- }
-
- /**
- * Ejecutar migración completa de BD
- *
- * @return array{success: bool, message: string, stats: array, log: array}
- */
- public function migrate(): array
- {
- $this->log('🚀 Iniciando migración de base de datos');
-
- try {
- // 1. Verificar que tablas legacy existen
- if (!$this->legacyTablesExist()) {
- return [
- 'success' => false,
- 'message' => 'Tablas legacy no encontradas. No hay nada que migrar.',
- 'stats' => [],
- 'log' => $this->log
- ];
- }
-
- $this->log('✓ Tablas legacy encontradas');
-
- // 2. Crear tablas v2 con nueva estructura
- $this->createV2Tables();
- $this->log('✓ Tablas v2 creadas con nueva estructura');
-
- // 3. Migrar datos de components
- $componentsStats = $this->migrateComponentsData();
- $this->log("✓ Components migrados: {$componentsStats['migrated']} registros");
-
- // 4. Migrar datos de defaults
- $defaultsStats = $this->migrateDefaultsData();
- $this->log("✓ Defaults migrados: {$defaultsStats['migrated']} registros");
-
- // 5. Validar integridad de datos
- $validation = $this->validateMigration();
-
- if (!$validation['success']) {
- throw new \RuntimeException(
- 'Validación de migración falló: ' . $validation['message']
- );
- }
-
- $this->log('✓ Validación de integridad exitosa');
-
- // 6. Hacer swap de tablas
- $this->swapTables();
- $this->log('✓ Swap de tablas completado (legacy → _backup, v2 → producción)');
-
- // 7. Guardar metadata de migración
- update_option('roi_theme_migration_date', current_time('mysql'));
- update_option('roi_theme_migration_stats', [
- 'components' => $componentsStats,
- 'defaults' => $defaultsStats
- ]);
-
- $this->log('✅ Migración completada exitosamente');
-
- return [
- 'success' => true,
- 'message' => 'Migración completada exitosamente',
- 'stats' => [
- 'components' => $componentsStats,
- 'defaults' => $defaultsStats,
- 'validation' => $validation
- ],
- 'log' => $this->log
- ];
-
- } catch (\Exception $e) {
- $this->log('❌ Error durante migración: ' . $e->getMessage());
-
- // Rollback en caso de error
- $this->rollback();
-
- return [
- 'success' => false,
- 'message' => 'Error durante migración: ' . $e->getMessage(),
- 'stats' => [],
- 'log' => $this->log
- ];
- }
- }
-
- /**
- * Verificar si las tablas legacy existen
- *
- * @return bool
- */
- private function legacyTablesExist(): bool
- {
- $legacy_components = $this->wpdb->prefix . 'roi_theme_components';
- $legacy_defaults = $this->wpdb->prefix . 'roi_theme_components_defaults';
-
- $components_exist = $this->wpdb->get_var(
- "SHOW TABLES LIKE '{$legacy_components}'"
- ) === $legacy_components;
-
- $defaults_exist = $this->wpdb->get_var(
- "SHOW TABLES LIKE '{$legacy_defaults}'"
- ) === $legacy_defaults;
-
- return $components_exist && $defaults_exist;
- }
-
- /**
- * Crear tablas v2 con nueva estructura
- *
- * @return void
- */
- private function createV2Tables(): void
- {
- require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
-
- $components_v2 = $this->wpdb->prefix . 'roi_theme_components_v2';
- $defaults_v2 = $this->wpdb->prefix . 'roi_theme_components_defaults_v2';
-
- // Estructura mejorada con config_group
- $table_structure = "
- id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
- component_name VARCHAR(50) NOT NULL,
- config_group VARCHAR(50) NOT NULL,
- config_key VARCHAR(100) NOT NULL,
- config_value TEXT NOT NULL,
- data_type VARCHAR(20) NOT NULL DEFAULT 'string',
- schema_version VARCHAR(10) NOT NULL DEFAULT '1.0.0',
- created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
- updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- PRIMARY KEY (id),
- UNIQUE KEY component_config (component_name, config_group, config_key),
- INDEX idx_component (component_name),
- INDEX idx_group (component_name, config_group),
- INDEX idx_schema_version (component_name, schema_version),
- INDEX idx_config_key (config_key)
- ";
-
- $sql_components = "CREATE TABLE {$components_v2} ({$table_structure}) {$this->charset_collate};";
- $sql_defaults = "CREATE TABLE {$defaults_v2} ({$table_structure}) {$this->charset_collate};";
-
- dbDelta($sql_components);
- dbDelta($sql_defaults);
- }
-
- /**
- * Migrar datos de components legacy a v2
- *
- * @return array{migrated: int, skipped: int, errors: array}
- */
- private function migrateComponentsData(): array
- {
- $legacy_table = $this->wpdb->prefix . 'roi_theme_components';
- $v2_table = $this->wpdb->prefix . 'roi_theme_components_v2';
-
- return $this->migrateTableData($legacy_table, $v2_table);
- }
-
- /**
- * Migrar datos de defaults legacy a v2
- *
- * @return array{migrated: int, skipped: int, errors: array}
- */
- private function migrateDefaultsData(): array
- {
- $legacy_table = $this->wpdb->prefix . 'roi_theme_components_defaults';
- $v2_table = $this->wpdb->prefix . 'roi_theme_components_defaults_v2';
-
- return $this->migrateTableData($legacy_table, $v2_table);
- }
-
- /**
- * Migrar datos de una tabla legacy a v2
- *
- * @param string $legacy_table Nombre de tabla legacy
- * @param string $v2_table Nombre de tabla v2
- * @return array{migrated: int, skipped: int, errors: array}
- */
- private function migrateTableData(string $legacy_table, string $v2_table): array
- {
- // Obtener todos los registros legacy
- $legacy_rows = $this->wpdb->get_results(
- "SELECT * FROM {$legacy_table}",
- ARRAY_A
- );
-
- if (empty($legacy_rows)) {
- return ['migrated' => 0, 'skipped' => 0, 'errors' => []];
- }
-
- $migrated = 0;
- $skipped = 0;
- $errors = [];
-
- foreach ($legacy_rows as $row) {
- try {
- // Inferir config_group desde config_key
- $group = $this->inferGroupFromKey($row['config_key']);
-
- // Preparar datos para inserción
- $data = [
- 'component_name' => $row['component_name'],
- 'config_group' => $group,
- 'config_key' => $row['config_key'],
- 'config_value' => $row['config_value'],
- 'data_type' => $row['data_type'],
- 'schema_version' => $row['version'] ?? '1.0.0',
- 'created_at' => $row['created_at'],
- 'updated_at' => $row['updated_at']
- ];
-
- // Insertar en tabla v2
- $result = $this->wpdb->insert(
- $v2_table,
- $data,
- ['%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s']
- );
-
- if ($result !== false) {
- $migrated++;
- } else {
- $skipped++;
- $errors[] = "Error migrando: {$row['component_name']}.{$row['config_key']}";
- }
-
- } catch (\Exception $e) {
- $skipped++;
- $errors[] = "Excepción migrando {$row['component_name']}.{$row['config_key']}: " . $e->getMessage();
- }
- }
-
- return [
- 'migrated' => $migrated,
- 'skipped' => $skipped,
- 'errors' => $errors
- ];
- }
-
- /**
- * Inferir grupo de configuración desde la clave
- *
- * HEURÍSTICA:
- * - enabled, visible_* → visibility
- * - message_*, cta_*, title_* → content
- * - *_color, *_height, *_width, *_size → styles
- * - Resto → general
- *
- * @param string $key Clave de configuración
- * @return string Grupo inferido
- */
- private function inferGroupFromKey(string $key): string
- {
- // Visibility
- if (in_array($key, ['enabled', 'visible_desktop', 'visible_mobile', 'visible_tablet'], true)) {
- return 'visibility';
- }
-
- // Content
- if (str_starts_with($key, 'message_') ||
- str_starts_with($key, 'cta_') ||
- str_starts_with($key, 'title_')) {
- return 'content';
- }
-
- // Styles
- if (str_ends_with($key, '_color') ||
- str_ends_with($key, '_height') ||
- str_ends_with($key, '_width') ||
- str_ends_with($key, '_size') ||
- str_ends_with($key, '_font')) {
- return 'styles';
- }
-
- // Fallback
- return 'general';
- }
-
- /**
- * Validar integridad de la migración
- *
- * @return array{success: bool, message: string, details: array}
- */
- private function validateMigration(): array
- {
- $legacy_components = $this->wpdb->prefix . 'roi_theme_components';
- $legacy_defaults = $this->wpdb->prefix . 'roi_theme_components_defaults';
- $v2_components = $this->wpdb->prefix . 'roi_theme_components_v2';
- $v2_defaults = $this->wpdb->prefix . 'roi_theme_components_defaults_v2';
-
- $details = [];
-
- // 1. Validar cantidad de registros components
- $legacy_count = $this->wpdb->get_var("SELECT COUNT(*) FROM {$legacy_components}");
- $v2_count = $this->wpdb->get_var("SELECT COUNT(*) FROM {$v2_components}");
-
- $details['components_count'] = [
- 'legacy' => (int) $legacy_count,
- 'v2' => (int) $v2_count,
- 'match' => $legacy_count == $v2_count
- ];
-
- if ($legacy_count != $v2_count) {
- return [
- 'success' => false,
- 'message' => "Mismatch en cantidad de registros components: legacy={$legacy_count}, v2={$v2_count}",
- 'details' => $details
- ];
- }
-
- // 2. Validar cantidad de registros defaults
- $legacy_defaults_count = $this->wpdb->get_var("SELECT COUNT(*) FROM {$legacy_defaults}");
- $v2_defaults_count = $this->wpdb->get_var("SELECT COUNT(*) FROM {$v2_defaults}");
-
- $details['defaults_count'] = [
- 'legacy' => (int) $legacy_defaults_count,
- 'v2' => (int) $v2_defaults_count,
- 'match' => $legacy_defaults_count == $v2_defaults_count
- ];
-
- if ($legacy_defaults_count != $v2_defaults_count) {
- return [
- 'success' => false,
- 'message' => "Mismatch en cantidad de registros defaults: legacy={$legacy_defaults_count}, v2={$v2_defaults_count}",
- 'details' => $details
- ];
- }
-
- // 3. Verificar componentes únicos
- $legacy_component_names = $this->wpdb->get_col(
- "SELECT DISTINCT component_name FROM {$legacy_components}"
- );
- $v2_component_names = $this->wpdb->get_col(
- "SELECT DISTINCT component_name FROM {$v2_components}"
- );
-
- $missing_components = array_diff($legacy_component_names, $v2_component_names);
-
- if (count($missing_components) > 0) {
- return [
- 'success' => false,
- 'message' => "Faltan componentes en v2: " . implode(', ', $missing_components),
- 'details' => $details
- ];
- }
-
- // 4. Verificar grupos válidos
- $invalid_groups = $this->wpdb->get_col(
- "SELECT DISTINCT config_group FROM {$v2_components}
- WHERE config_group NOT IN ('visibility', 'content', 'styles', 'general')"
- );
-
- if (count($invalid_groups) > 0) {
- return [
- 'success' => false,
- 'message' => "Grupos inválidos encontrados: " . implode(', ', $invalid_groups),
- 'details' => $details
- ];
- }
-
- return [
- 'success' => true,
- 'message' => "Validación exitosa: {$legacy_count} components + {$legacy_defaults_count} defaults migrados correctamente",
- 'details' => $details
- ];
- }
-
- /**
- * Hacer swap de tablas (legacy → backup, v2 → producción)
- *
- * @return void
- */
- private function swapTables(): void
- {
- // Swap components
- $this->wpdb->query(
- "RENAME TABLE
- {$this->wpdb->prefix}roi_theme_components TO {$this->wpdb->prefix}roi_theme_components_backup,
- {$this->wpdb->prefix}roi_theme_components_v2 TO {$this->wpdb->prefix}roi_theme_components"
- );
-
- // Swap defaults
- $this->wpdb->query(
- "RENAME TABLE
- {$this->wpdb->prefix}roi_theme_components_defaults TO {$this->wpdb->prefix}roi_theme_components_defaults_backup,
- {$this->wpdb->prefix}roi_theme_components_defaults_v2 TO {$this->wpdb->prefix}roi_theme_components_defaults"
- );
-
- // Guardar timestamp de backup
- update_option('roi_theme_migration_backup_date', current_time('mysql'));
- }
-
- /**
- * Rollback: Eliminar tablas v2 en caso de error
- *
- * @return void
- */
- private function rollback(): void
- {
- $this->log('⚠️ Ejecutando rollback...');
-
- // Eliminar tablas v2 si existen
- $this->wpdb->query("DROP TABLE IF EXISTS {$this->wpdb->prefix}roi_theme_components_v2");
- $this->wpdb->query("DROP TABLE IF EXISTS {$this->wpdb->prefix}roi_theme_components_defaults_v2");
-
- $this->log('✓ Rollback completado: tablas v2 eliminadas');
- }
-
- /**
- * Limpiar tablas de backup (ejecutar después de validar migración)
- *
- * @return void
- */
- public function cleanupBackup(): void
- {
- $this->log('🗑️ Eliminando tablas de backup...');
-
- $this->wpdb->query("DROP TABLE IF EXISTS {$this->wpdb->prefix}roi_theme_components_backup");
- $this->wpdb->query("DROP TABLE IF EXISTS {$this->wpdb->prefix}roi_theme_components_defaults_backup");
-
- delete_option('roi_theme_migration_backup_date');
-
- $this->log('✓ Tablas de backup eliminadas');
- }
-
- /**
- * Agregar entrada al log
- *
- * @param string $message Mensaje a registrar
- * @return void
- */
- private function log(string $message): void
- {
- $timestamp = current_time('Y-m-d H:i:s');
- $entry = "[{$timestamp}] {$message}";
-
- $this->log[] = $entry;
- error_log('ROI Theme Migration: ' . $message);
- }
-
- /**
- * Obtener log completo de operaciones
- *
- * @return array
- */
- public function getLog(): array
- {
- return $this->log;
- }
-}
diff --git a/src/Infrastructure/Persistence/WordPress/Repositories/.gitkeep b/src/Infrastructure/Persistence/WordPress/Repositories/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/Infrastructure/Presentation/Admin/FormBuilders/.gitkeep b/src/Infrastructure/Presentation/Admin/FormBuilders/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/Infrastructure/Presentation/Public/Renderers/.gitkeep b/src/Infrastructure/Presentation/Public/Renderers/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/Infrastructure/README.md b/src/Infrastructure/README.md
deleted file mode 100644
index 1757d0b9..00000000
--- a/src/Infrastructure/README.md
+++ /dev/null
@@ -1,46 +0,0 @@
-# Capa de Infraestructura
-
-## Propósito
-
-La capa de Infraestructura **implementa** los detalles técnicos: persistencia, APIs, servicios externos, frameworks. Es la capa más externa y depende de las capas internas.
-
-## Principios
-
-1. **Implementación**: Implementa interfaces definidas en Dominio/Aplicación
-2. **Frameworks**: Aquí vive el código específico de WordPress
-3. **Adaptación**: Adapta datos entre formatos externos e internos
-4. **Dependencia Invertida**: Depende de abstracciones del dominio
-
-## Estructura
-
-```
-Infrastructure/
-├── Persistence/ # Acceso a datos
-│ └── WordPress/
-│ ├── WordPressComponentRepository.php
-│ └── DatabaseMigrator.php
-├── API/ # Controllers/Endpoints
-│ └── WordPress/
-│ ├── AjaxController.php
-│ └── RestController.php
-├── Services/ # Servicios de infraestructura
-│ ├── WordPressValidationService.php
-│ ├── WordPressCacheService.php
-│ └── WordPressComponentFacade.php
-├── DI/ # Dependency Injection
-│ └── DIContainer.php
-└── UI/ # Vistas y assets
- ├── Views/
- │ └── components/
- └── Assets/
- ├── css/
- └── js/
-```
-
-## Reglas
-
-1. **SÍ** usar código de WordPress aquí
-2. **SÍ** implementar interfaces del dominio/aplicación
-3. **SÍ** adaptar datos entre WordPress y entidades
-4. **NO** exponer detalles de implementación hacia arriba
-5. **NO** contener lógica de negocio
diff --git a/src/Infrastructure/UI/Assets/.gitkeep b/src/Infrastructure/UI/Assets/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/Infrastructure/UI/Views/.gitkeep b/src/Infrastructure/UI/Views/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/E2E/.gitkeep b/tests/E2E/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/Integration/.gitkeep b/tests/Integration/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/Integration/Infrastructure/DatabaseMigratorTest.php b/tests/Integration/Infrastructure/DatabaseMigratorTest.php
deleted file mode 100644
index 50c4a986..00000000
--- a/tests/Integration/Infrastructure/DatabaseMigratorTest.php
+++ /dev/null
@@ -1,365 +0,0 @@
-markTestSkipped('WordPress not loaded - skipping integration tests');
- return;
- }
-
- $this->wpdb = $wpdb;
- $this->prefix = $wpdb->prefix;
- $this->migrator = new DatabaseMigrator($wpdb);
-
- // Limpiar tablas anteriores
- $this->cleanupTables();
-
- // Crear tablas legacy con datos de prueba
- $this->createLegacyTables();
- $this->seedLegacyData();
- }
-
- /**
- * Teardown después de cada test
- */
- protected function tearDown(): void
- {
- $this->cleanupTables();
- }
-
- /**
- * Test: La migración crea tablas v2 correctamente
- *
- * @test
- */
- public function it_creates_v2_tables(): void
- {
- $result = $this->migrator->migrate();
-
- $this->assertTrue($result['success'], $result['message']);
-
- // Verificar que tabla components existe
- $components_table = $this->prefix . 'roi_theme_components';
- $table_exists = $this->wpdb->get_var(
- "SHOW TABLES LIKE '{$components_table}'"
- ) === $components_table;
-
- $this->assertTrue($table_exists, 'Tabla components no existe después de migración');
-
- // Verificar que tabla defaults existe
- $defaults_table = $this->prefix . 'roi_theme_components_defaults';
- $table_exists = $this->wpdb->get_var(
- "SHOW TABLES LIKE '{$defaults_table}'"
- ) === $defaults_table;
-
- $this->assertTrue($table_exists, 'Tabla defaults no existe después de migración');
- }
-
- /**
- * Test: La migración preserva la cantidad de registros
- *
- * @test
- */
- public function it_preserves_record_count(): void
- {
- // Contar antes de migración
- $legacy_count = $this->wpdb->get_var(
- "SELECT COUNT(*) FROM {$this->prefix}roi_theme_components"
- );
-
- // Ejecutar migración
- $result = $this->migrator->migrate();
- $this->assertTrue($result['success']);
-
- // Contar después de migración
- $v2_count = $this->wpdb->get_var(
- "SELECT COUNT(*) FROM {$this->prefix}roi_theme_components"
- );
-
- $this->assertEquals(
- $legacy_count,
- $v2_count,
- "Cantidad de registros no coincide: legacy={$legacy_count}, v2={$v2_count}"
- );
- }
-
- /**
- * Test: La migración infiere grupos correctamente
- *
- * @test
- */
- public function it_infers_config_groups_correctly(): void
- {
- $result = $this->migrator->migrate();
- $this->assertTrue($result['success']);
-
- // Verificar que "enabled" se migró a grupo "visibility"
- $group = $this->wpdb->get_var(
- "SELECT config_group FROM {$this->prefix}roi_theme_components
- WHERE config_key = 'enabled' LIMIT 1"
- );
-
- $this->assertEquals('visibility', $group);
-
- // Verificar que "message_text" se migró a grupo "content"
- $group = $this->wpdb->get_var(
- "SELECT config_group FROM {$this->prefix}roi_theme_components
- WHERE config_key = 'message_text' LIMIT 1"
- );
-
- $this->assertEquals('content', $group);
-
- // Verificar que "background_color" se migró a grupo "styles"
- $group = $this->wpdb->get_var(
- "SELECT config_group FROM {$this->prefix}roi_theme_components
- WHERE config_key = 'background_color' LIMIT 1"
- );
-
- $this->assertEquals('styles', $group);
- }
-
- /**
- * Test: La migración crea backup de tablas legacy
- *
- * @test
- */
- public function it_creates_backup_tables(): void
- {
- $result = $this->migrator->migrate();
- $this->assertTrue($result['success']);
-
- // Verificar que tabla backup existe
- $backup_table = $this->prefix . 'roi_theme_components_backup';
- $table_exists = $this->wpdb->get_var(
- "SHOW TABLES LIKE '{$backup_table}'"
- ) === $backup_table;
-
- $this->assertTrue($table_exists, 'Tabla backup no fue creada');
-
- // Verificar que backup tiene datos
- $backup_count = $this->wpdb->get_var(
- "SELECT COUNT(*) FROM {$backup_table}"
- );
-
- $this->assertGreaterThan(0, $backup_count, 'Tabla backup está vacía');
- }
-
- /**
- * Test: Rollback elimina tablas v2 en caso de error
- *
- * @test
- */
- public function it_rolls_back_on_error(): void
- {
- // Crear escenario de error: tabla legacy vacía después de crear v2
- $this->wpdb->query("DELETE FROM {$this->prefix}roi_theme_components");
-
- $result = $this->migrator->migrate();
-
- // La migración debe fallar por mismatch de conteo
- $this->assertFalse($result['success']);
-
- // Verificar que tablas v2 fueron eliminadas
- $v2_table = $this->prefix . 'roi_theme_components_v2';
- $table_exists = $this->wpdb->get_var(
- "SHOW TABLES LIKE '{$v2_table}'"
- ) === $v2_table;
-
- $this->assertFalse($table_exists, 'Tabla v2 no fue eliminada en rollback');
- }
-
- /**
- * Test: cleanup_backup elimina tablas de respaldo
- *
- * @test
- */
- public function it_removes_backup_tables(): void
- {
- // Ejecutar migración
- $result = $this->migrator->migrate();
- $this->assertTrue($result['success']);
-
- // Verificar que backup existe
- $backup_table = $this->prefix . 'roi_theme_components_backup';
- $table_exists = $this->wpdb->get_var(
- "SHOW TABLES LIKE '{$backup_table}'"
- ) === $backup_table;
- $this->assertTrue($table_exists);
-
- // Ejecutar cleanup
- $this->migrator->cleanupBackup();
-
- // Verificar que backup fue eliminado
- $table_exists = $this->wpdb->get_var(
- "SHOW TABLES LIKE '{$backup_table}'"
- ) === $backup_table;
-
- $this->assertFalse($table_exists, 'Tabla backup no fue eliminada');
- }
-
- /**
- * Helper: Crear tablas legacy para testing
- */
- private function createLegacyTables(): void
- {
- if (!defined('ABSPATH')) {
- return;
- }
-
- require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
-
- $charset_collate = $this->wpdb->get_charset_collate();
-
- $components_sql = "CREATE TABLE {$this->prefix}roi_theme_components (
- id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
- component_name VARCHAR(50) NOT NULL,
- config_key VARCHAR(100) NOT NULL,
- config_value TEXT NOT NULL,
- data_type VARCHAR(20) NOT NULL DEFAULT 'string',
- version VARCHAR(10) NOT NULL DEFAULT '1.0.0',
- created_at DATETIME NOT NULL,
- updated_at DATETIME NOT NULL,
- PRIMARY KEY (id),
- UNIQUE KEY component_config (component_name, config_key)
- ) {$charset_collate};";
-
- $defaults_sql = "CREATE TABLE {$this->prefix}roi_theme_components_defaults (
- id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
- component_name VARCHAR(50) NOT NULL,
- config_key VARCHAR(100) NOT NULL,
- config_value TEXT NOT NULL,
- data_type VARCHAR(20) NOT NULL DEFAULT 'string',
- version VARCHAR(10) NOT NULL DEFAULT '1.0.0',
- created_at DATETIME NOT NULL,
- updated_at DATETIME NOT NULL,
- PRIMARY KEY (id),
- UNIQUE KEY component_config (component_name, config_key)
- ) {$charset_collate};";
-
- dbDelta($components_sql);
- dbDelta($defaults_sql);
- }
-
- /**
- * Helper: Insertar datos de prueba en tablas legacy
- */
- private function seedLegacyData(): void
- {
- $timestamp = current_time('mysql');
-
- // Components
- $components_data = [
- ['top_bar', 'enabled', '1', 'boolean'],
- ['top_bar', 'message_text', 'Welcome!', 'string'],
- ['top_bar', 'background_color', '#000000', 'string'],
- ['footer', 'enabled', '1', 'boolean'],
- ['footer', 'cta_url', 'https://example.com', 'string'],
- ['footer', 'cta_text', 'Click here', 'string'],
- ];
-
- foreach ($components_data as $data) {
- $this->wpdb->insert(
- $this->prefix . 'roi_theme_components',
- [
- 'component_name' => $data[0],
- 'config_key' => $data[1],
- 'config_value' => $data[2],
- 'data_type' => $data[3],
- 'version' => '1.0.0',
- 'created_at' => $timestamp,
- 'updated_at' => $timestamp
- ],
- ['%s', '%s', '%s', '%s', '%s', '%s', '%s']
- );
- }
-
- // Defaults (misma estructura)
- $this->wpdb->insert(
- $this->prefix . 'roi_theme_components_defaults',
- [
- 'component_name' => 'top_bar',
- 'config_key' => 'enabled',
- 'config_value' => '1',
- 'data_type' => 'boolean',
- 'version' => '1.0.0',
- 'created_at' => $timestamp,
- 'updated_at' => $timestamp
- ],
- ['%s', '%s', '%s', '%s', '%s', '%s', '%s']
- );
- }
-
- /**
- * Helper: Limpiar todas las tablas del test
- */
- private function cleanupTables(): void
- {
- $tables = [
- 'roi_theme_components',
- 'roi_theme_components_defaults',
- 'roi_theme_components_v2',
- 'roi_theme_components_defaults_v2',
- 'roi_theme_components_backup',
- 'roi_theme_components_defaults_backup'
- ];
-
- foreach ($tables as $table) {
- $this->wpdb->query("DROP TABLE IF EXISTS {$this->prefix}{$table}");
- }
-
- // Limpiar opciones
- delete_option('roi_theme_migration_date');
- delete_option('roi_theme_migration_backup_date');
- delete_option('roi_theme_migration_stats');
- }
-}
diff --git a/tests/Unit/Application/UseCases/.gitkeep b/tests/Unit/Application/UseCases/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/Unit/Domain/Component/.gitkeep b/tests/Unit/Domain/Component/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/Unit/ExampleTest.php b/tests/Unit/ExampleTest.php
deleted file mode 100644
index dd5fb5ce..00000000
--- a/tests/Unit/ExampleTest.php
+++ /dev/null
@@ -1,30 +0,0 @@
-assertTrue(true, 'PHPUnit está funcionando correctamente');
- }
-
- public function testPhpVersion(): void
- {
- $version = PHP_VERSION;
- $this->assertGreaterThanOrEqual('8.0.0', $version, 'PHP debe ser versión 8.0 o superior');
- }
-
- public function testComposerAutoloadIsLoaded(): void
- {
- $this->assertTrue(
- class_exists('PHPUnit\Framework\TestCase'),
- 'Composer autoload está funcionando'
- );
- }
-}
diff --git a/tests/Unit/Infrastructure/DI/DIContainerTest.php b/tests/Unit/Infrastructure/DI/DIContainerTest.php
deleted file mode 100644
index 10f00289..00000000
--- a/tests/Unit/Infrastructure/DI/DIContainerTest.php
+++ /dev/null
@@ -1,175 +0,0 @@
-assertSame($instance1, $instance2, 'getInstance() should return the same instance');
- }
-
- /**
- * @test
- */
- public function it_should_prevent_cloning(): void
- {
- $this->expectError();
- $this->expectErrorMessage('Call to private');
-
- $container = DIContainer::getInstance();
- $clone = clone $container; // This should trigger an error
- }
-
- /**
- * @test
- */
- public function it_should_prevent_unserialization(): void
- {
- $this->expectException(Exception::class);
- $this->expectExceptionMessage('Cannot unserialize singleton');
-
- $container = DIContainer::getInstance();
- $serialized = serialize($container);
- unserialize($serialized);
- }
-
- /**
- * @test
- */
- public function it_should_register_and_retrieve_service(): void
- {
- $container = DIContainer::getInstance();
- $service = new \stdClass();
- $service->name = 'Test Service';
-
- $container->set('test_service', $service);
-
- $this->assertTrue($container->has('test_service'), 'Container should have the registered service');
- $this->assertSame($service, $container->get('test_service'), 'Should retrieve the same service instance');
- }
-
- /**
- * @test
- */
- public function it_should_return_null_for_non_existent_service(): void
- {
- $container = DIContainer::getInstance();
-
- $this->assertFalse($container->has('non_existent'), 'Container should not have non-existent service');
- $this->assertNull($container->get('non_existent'), 'Should return null for non-existent service');
- }
-
- /**
- * @test
- */
- public function it_should_throw_exception_for_unimplemented_component_repository(): void
- {
- $this->expectException(RuntimeException::class);
- $this->expectExceptionMessage('ComponentRepository not implemented yet');
-
- $container = DIContainer::getInstance();
- $container->getComponentRepository();
- }
-
- /**
- * @test
- */
- public function it_should_throw_exception_for_unimplemented_validation_service(): void
- {
- $this->expectException(RuntimeException::class);
- $this->expectExceptionMessage('ValidationService not implemented yet');
-
- $container = DIContainer::getInstance();
- $container->getValidationService();
- }
-
- /**
- * @test
- */
- public function it_should_throw_exception_for_unimplemented_cache_service(): void
- {
- $this->expectException(RuntimeException::class);
- $this->expectExceptionMessage('CacheService not implemented yet');
-
- $container = DIContainer::getInstance();
- $container->getCacheService();
- }
-
- /**
- * @test
- */
- public function it_should_reset_singleton_instance(): void
- {
- $instance1 = DIContainer::getInstance();
- $instance1->set('test', new \stdClass());
-
- DIContainer::reset();
-
- $instance2 = DIContainer::getInstance();
-
- $this->assertNotSame($instance1, $instance2, 'Reset should create a new instance');
- $this->assertFalse($instance2->has('test'), 'New instance should not have old services');
- }
-
- /**
- * @test
- */
- public function it_should_manage_multiple_services(): void
- {
- $container = DIContainer::getInstance();
-
- $service1 = new \stdClass();
- $service1->name = 'Service 1';
-
- $service2 = new \stdClass();
- $service2->name = 'Service 2';
-
- $service3 = new \stdClass();
- $service3->name = 'Service 3';
-
- $container->set('service1', $service1);
- $container->set('service2', $service2);
- $container->set('service3', $service3);
-
- $this->assertTrue($container->has('service1'));
- $this->assertTrue($container->has('service2'));
- $this->assertTrue($container->has('service3'));
-
- $this->assertSame($service1, $container->get('service1'));
- $this->assertSame($service2, $container->get('service2'));
- $this->assertSame($service3, $container->get('service3'));
- }
-}
diff --git a/tests/Unit/Infrastructure/Persistence/.gitkeep b/tests/Unit/Infrastructure/Persistence/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/Unit/Infrastructure/Services/.gitkeep b/tests/Unit/Infrastructure/Services/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/_bootstrap/bootstrap.php b/tests/_bootstrap/bootstrap.php
deleted file mode 100644
index 5deafdc4..00000000
--- a/tests/_bootstrap/bootstrap.php
+++ /dev/null
@@ -1,21 +0,0 @@
-