fix(structure): Correct case-sensitivity for Linux compatibility
Rename folders to match PHP PSR-4 autoloading conventions: - schemas → Schemas - shared → Shared - Wordpress → WordPress (in all locations) Fixes deployment issues on Linux servers where filesystem is case-sensitive. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
215
Shared/Infrastructure/Validators/Phase02Validator.php
Normal file
215
Shared/Infrastructure/Validators/Phase02Validator.php
Normal file
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ROITheme\Shared\Infrastructure\Validators;
|
||||
|
||||
/**
|
||||
* Validador de Fase 02: Sincronización JSON→BD
|
||||
*
|
||||
* Valida que:
|
||||
* - Schema JSON existe y es válido
|
||||
* - Tabla de BD existe
|
||||
* - Todos los campos del JSON están sincronizados en BD
|
||||
* - No hay campos huérfanos en BD
|
||||
* - is_editable coincide entre JSON y BD
|
||||
* - No hay duplicados
|
||||
*/
|
||||
final class Phase02Validator implements PhaseValidatorInterface
|
||||
{
|
||||
private const TABLE_NAME = 'wp_roi_theme_component_settings';
|
||||
|
||||
public function validate(string $componentName, string $themePath): ValidationResult
|
||||
{
|
||||
global $wpdb;
|
||||
|
||||
$result = new ValidationResult();
|
||||
|
||||
$result->addInfo("Validando sincronización JSON→BD para: {$componentName}");
|
||||
|
||||
// 1. Verificar que schema JSON existe
|
||||
$schemaPath = $themePath . '/Schemas/' . $componentName . '.json';
|
||||
|
||||
if (!file_exists($schemaPath)) {
|
||||
$result->addError("Schema JSON no encontrado: {$schemaPath}");
|
||||
$result->addInfo("Ejecutar primero: wp roi-theme sync-component {$componentName}");
|
||||
return $result;
|
||||
}
|
||||
|
||||
// 2. Parsear JSON
|
||||
$jsonContent = file_get_contents($schemaPath);
|
||||
$schema = json_decode($jsonContent, true);
|
||||
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
$result->addError("JSON inválido: " . json_last_error_msg());
|
||||
return $result;
|
||||
}
|
||||
|
||||
// 3. Verificar que tabla existe
|
||||
$tableName = $wpdb->prefix . 'roi_theme_component_settings';
|
||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
|
||||
$tableExists = $wpdb->get_var($wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = %s",
|
||||
$tableName
|
||||
));
|
||||
|
||||
if ($tableExists == 0) {
|
||||
$result->addError("Tabla '{$tableName}' no existe en la base de datos");
|
||||
$result->addInfo("La tabla debería crearse automáticamente en functions.php");
|
||||
return $result;
|
||||
}
|
||||
|
||||
// 4. Obtener todos los campos del JSON
|
||||
$jsonFields = $this->extractFieldsFromSchema($schema);
|
||||
$totalJsonFields = count($jsonFields);
|
||||
|
||||
// 5. Obtener todos los registros de BD para este componente
|
||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
|
||||
$dbRecords = $wpdb->get_results($wpdb->prepare(
|
||||
"SELECT component_name, group_name, attribute_name, is_editable FROM {$tableName} WHERE component_name = %s",
|
||||
$componentName
|
||||
), ARRAY_A);
|
||||
|
||||
$totalDbRecords = count($dbRecords);
|
||||
|
||||
// 6. Validar sincronización
|
||||
$this->validateSync($componentName, $jsonFields, $dbRecords, $result);
|
||||
|
||||
// 7. Validar no hay duplicados
|
||||
$this->validateNoDuplicates($componentName, $tableName, $wpdb, $result);
|
||||
|
||||
// Estadísticas
|
||||
$result->setStat('Schema JSON', "schemas/{$componentName}.json");
|
||||
$result->setStat('Campos en JSON', $totalJsonFields);
|
||||
$result->setStat('Registros en BD', $totalDbRecords);
|
||||
$result->setStat('Tabla BD', $tableName);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extrae todos los campos del schema JSON
|
||||
*
|
||||
* @param array $schema
|
||||
* @return array Array de arrays con ['group' => '', 'attribute' => '', 'editable' => bool]
|
||||
*/
|
||||
private function extractFieldsFromSchema(array $schema): array
|
||||
{
|
||||
$fields = [];
|
||||
|
||||
if (!isset($schema['groups'])) {
|
||||
return $fields;
|
||||
}
|
||||
|
||||
foreach ($schema['groups'] as $groupName => $group) {
|
||||
if (!isset($group['fields'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($group['fields'] as $attributeName => $field) {
|
||||
$fields[] = [
|
||||
'group' => $groupName,
|
||||
'attribute' => $attributeName,
|
||||
'editable' => $field['editable'] ?? false,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
private function validateSync(string $componentName, array $jsonFields, array $dbRecords, ValidationResult $result): void
|
||||
{
|
||||
// Crear índice de registros de BD para búsqueda rápida
|
||||
$dbIndex = [];
|
||||
foreach ($dbRecords as $record) {
|
||||
$key = $record['group_name'] . '.' . $record['attribute_name'];
|
||||
$dbIndex[$key] = $record;
|
||||
}
|
||||
|
||||
// Validar que cada campo del JSON está en BD
|
||||
$missingInDb = [];
|
||||
$editableMismatch = [];
|
||||
|
||||
foreach ($jsonFields as $field) {
|
||||
$key = $field['group'] . '.' . $field['attribute'];
|
||||
|
||||
if (!isset($dbIndex[$key])) {
|
||||
$missingInDb[] = $key;
|
||||
} else {
|
||||
// Validar is_editable coincide
|
||||
$dbEditable = (bool) $dbIndex[$key]['is_editable'];
|
||||
$jsonEditable = $field['editable'];
|
||||
|
||||
if ($dbEditable !== $jsonEditable) {
|
||||
$editableMismatch[] = "{$key} (JSON: " . ($jsonEditable ? 'true' : 'false') .
|
||||
", BD: " . ($dbEditable ? 'true' : 'false') . ")";
|
||||
}
|
||||
|
||||
// Remover de índice para detectar huérfanos
|
||||
unset($dbIndex[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
// Campos faltantes en BD
|
||||
if (!empty($missingInDb)) {
|
||||
foreach ($missingInDb as $field) {
|
||||
$result->addError("Campo '{$field}' existe en JSON pero NO en BD");
|
||||
}
|
||||
$result->addInfo("Ejecutar: wp roi-theme sync-component {$componentName}");
|
||||
}
|
||||
|
||||
// Campos huérfanos en BD (no están en JSON)
|
||||
if (!empty($dbIndex)) {
|
||||
foreach ($dbIndex as $key => $record) {
|
||||
$result->addWarning("Campo '{$key}' existe en BD pero NO en JSON (campo huérfano)");
|
||||
}
|
||||
}
|
||||
|
||||
// is_editable no coincide
|
||||
if (!empty($editableMismatch)) {
|
||||
foreach ($editableMismatch as $mismatch) {
|
||||
$result->addError("Campo {$mismatch} tiene is_editable diferente entre JSON y BD");
|
||||
}
|
||||
$result->addInfo("Ejecutar: wp roi-theme sync-component {$componentName}");
|
||||
}
|
||||
|
||||
// Si todo está sincronizado
|
||||
if (empty($missingInDb) && empty($editableMismatch) && empty($dbIndex)) {
|
||||
$result->addInfo("✓ Todos los campos están sincronizados correctamente");
|
||||
}
|
||||
}
|
||||
|
||||
private function validateNoDuplicates(string $componentName, string $tableName, $wpdb, ValidationResult $result): void
|
||||
{
|
||||
// Buscar duplicados
|
||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
|
||||
$duplicates = $wpdb->get_results($wpdb->prepare(
|
||||
"SELECT component_name, group_name, attribute_name, COUNT(*) as count
|
||||
FROM {$tableName}
|
||||
WHERE component_name = %s
|
||||
GROUP BY component_name, group_name, attribute_name
|
||||
HAVING count > 1",
|
||||
$componentName
|
||||
), ARRAY_A);
|
||||
|
||||
if (!empty($duplicates)) {
|
||||
foreach ($duplicates as $dup) {
|
||||
$result->addError(
|
||||
"Duplicado en BD: {$dup['group_name']}.{$dup['attribute_name']} " .
|
||||
"({$dup['count']} registros)"
|
||||
);
|
||||
}
|
||||
$result->addInfo("El constraint UNIQUE debería prevenir duplicados. Revisar integridad de BD.");
|
||||
}
|
||||
}
|
||||
|
||||
public function getPhaseNumber(): int|string
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
public function getPhaseDescription(): string
|
||||
{
|
||||
return 'JSON→DB Sync';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user