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:
FrankZamora
2025-11-26 22:53:34 -06:00
parent a2548ab5c2
commit 90863cd8f5
92 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,388 @@
<?php
declare(strict_types=1);
namespace ROITheme\Shared\Domain\ValueObjects;
use ROITheme\Shared\Domain\Exceptions\InvalidComponentException;
/**
* ComponentConfiguration - Value Object inmutable para configuración de componente
*
* RESPONSABILIDAD: Representar la configuración completa de un componente agrupada por categorías
*
* REGLAS DE NEGOCIO:
* - La configuración se organiza en grupos: visibility, content, styles, general
* - Cada grupo contiene pares clave-valor
* - Los valores pueden ser: string, boolean, integer, float, array
* - Si existe cta_url debe existir cta_text (y viceversa)
* - Los colores deben estar en formato hexadecimal válido
*
* INVARIANTES:
* - Una vez creada, la configuración no puede cambiar (inmutable)
* - La estructura de grupos siempre está presente (aunque vacía)
* - Los tipos de datos se preservan correctamente
*
* ESTRUCTURA:
* ```php
* [
* 'visibility' => [
* 'enabled' => true,
* 'visible_desktop' => true,
* 'visible_mobile' => false
* ],
* 'content' => [
* 'message_text' => 'Welcome!',
* 'cta_text' => 'Click here',
* 'cta_url' => 'https://example.com'
* ],
* 'styles' => [
* 'background_color' => '#000000',
* 'text_color' => '#ffffff',
* 'height' => 50
* ],
* 'general' => [
* 'priority' => 10,
* 'version' => '1.0.0'
* ]
* ]
* ```
*
* @package ROITheme\Shared\Domain\ValueObjects
*/
final readonly class ComponentConfiguration
{
/**
* Grupos de configuración válidos
*
* Basado en 10.00-flujo-de-trabajo-fuente-de-verdad.md líneas 304-348
* 12 grupos estándar + grupos adicionales en uso
*/
private const VALID_GROUPS = [
// 12 Grupos estándar del flujo de trabajo
'visibility', // Control de visibilidad
'content', // Textos y contenido
'typography', // Estilos de texto y semántica HTML
'colors', // Paleta de colores
'spacing', // Márgenes y padding
'visual_effects', // Efectos visuales decorativos
'behavior', // Comportamiento interactivo
'layout', // Estructura y posicionamiento
'links', // Enlaces y URLs
'icons', // Configuración de íconos
'media', // Multimedia
'forms', // Formularios
// Grupos adicionales/legacy en uso
'styles', // Legacy: combina colors, spacing, visual_effects
'general', // Configuración general del componente
'menu', // Específico para navbar
'categories', // Categorización
'title', // Título del componente
'networks', // Específico para social-share
// Grupos específicos para contact-form
'contact_info', // Info de contacto (teléfono, email, ubicación)
'form_labels', // Labels y placeholders del formulario
'integration', // Configuración de webhook
'messages', // Mensajes de éxito/error/validación
// Grupos específicos para footer
'widget_1', // Widget 1 del footer (menú)
'widget_1b', // Widget 1B del footer (menú secundario columna 1)
'widget_2', // Widget 2 del footer (menú)
'widget_3', // Widget 3 del footer (menú)
'newsletter', // Sección newsletter del footer
'footer_bottom', // Pie del footer (copyright)
];
/**
* @param array $configuration Configuración agrupada
* @throws InvalidComponentException
*/
public function __construct(private array $configuration)
{
$this->validate();
}
/**
* Obtener toda la configuración
*
* @return array
*/
public function all(): array
{
return $this->configuration;
}
/**
* Obtener configuración de un grupo específico
*
* @param string $group Nombre del grupo (visibility, content, styles, general)
* @return array
*/
public function getGroup(string $group): array
{
return $this->configuration[$group] ?? [];
}
/**
* Obtener valor de una clave específica dentro de un grupo
*
* @param string $group Nombre del grupo
* @param string $key Clave de configuración
* @param mixed $default Valor por defecto si no existe
* @return mixed
*/
public function get(string $group, string $key, mixed $default = null): mixed
{
return $this->configuration[$group][$key] ?? $default;
}
/**
* Verificar si existe una clave en un grupo
*
* @param string $group
* @param string $key
* @return bool
*/
public function has(string $group, string $key): bool
{
return isset($this->configuration[$group][$key]);
}
/**
* Verificar si el componente tiene CTA URL
*
* @return bool
*/
public function hasCTAURL(): bool
{
return $this->has('content', 'cta_url') && !empty($this->get('content', 'cta_url'));
}
/**
* Verificar si el componente tiene CTA text
*
* @return bool
*/
public function hasCTAText(): bool
{
return $this->has('content', 'cta_text') && !empty($this->get('content', 'cta_text'));
}
/**
* Verificar si el componente está habilitado
*
* @return bool
*/
public function isEnabled(): bool
{
return (bool) $this->get('visibility', 'enabled', false);
}
/**
* Crear nueva configuración con un valor actualizado
* (Inmutabilidad: retorna nueva instancia)
*
* @param string $group
* @param string $key
* @param mixed $value
* @return self
*/
public function withValue(string $group, string $key, mixed $value): self
{
$newConfiguration = $this->configuration;
if (!isset($newConfiguration[$group])) {
$newConfiguration[$group] = [];
}
$newConfiguration[$group][$key] = $value;
return new self($newConfiguration);
}
/**
* Crear nueva configuración con un grupo actualizado
* (Inmutabilidad: retorna nueva instancia)
*
* @param string $group
* @param array $values
* @return self
*/
public function withGroup(string $group, array $values): self
{
$newConfiguration = $this->configuration;
$newConfiguration[$group] = $values;
return new self($newConfiguration);
}
/**
* Validar reglas de negocio de la configuración
*
* @throws InvalidComponentException
* @return void
*/
private function validate(): void
{
// Regla 1: Debe ser un array
if (!is_array($this->configuration)) {
throw new InvalidComponentException('Configuration must be an array');
}
// Regla 2: Los grupos deben ser válidos
foreach (array_keys($this->configuration) as $group) {
if (!in_array($group, self::VALID_GROUPS, true)) {
throw new InvalidComponentException(
sprintf(
'Invalid configuration group "%s". Valid groups are: %s',
$group,
implode(', ', self::VALID_GROUPS)
)
);
}
}
// Regla 3: Si existe cta_url debe existir cta_text (y viceversa)
if ($this->hasCTAURL() && !$this->hasCTAText()) {
throw new InvalidComponentException('CTA URL requires CTA text');
}
if ($this->hasCTAText() && !$this->hasCTAURL()) {
throw new InvalidComponentException('CTA text requires CTA URL');
}
// Regla 4: Los colores deben ser hexadecimales válidos
$this->validateColors();
}
/**
* Validar que los colores estén en formato hexadecimal
*
* @throws InvalidComponentException
* @return void
*/
private function validateColors(): void
{
$styles = $this->getGroup('styles');
foreach ($styles as $key => $value) {
if (str_ends_with($key, '_color') && !empty($value)) {
if (!preg_match('/^#[0-9A-Fa-f]{6}$/', $value)) {
throw new InvalidComponentException(
sprintf(
'Color "%s" must be in hexadecimal format (e.g., #000000). Got: %s',
$key,
$value
)
);
}
}
}
}
/**
* Crear desde array flat (legacy)
*
* Convierte de:
* ```php
* [
* 'enabled' => true,
* 'message_text' => 'Hello',
* 'background_color' => '#000'
* ]
* ```
*
* A estructura agrupada.
*
* @param array $flatConfig
* @return self
*/
public static function fromFlat(array $flatConfig): self
{
$grouped = [
'visibility' => [],
'content' => [],
'styles' => [],
'general' => []
];
foreach ($flatConfig as $key => $value) {
$group = self::inferGroup($key);
$grouped[$group][$key] = $value;
}
return new self($grouped);
}
/**
* Crear desde array agrupado
*
* Espera estructura ya agrupada:
* ```php
* [
* 'visibility' => ['enabled' => true, ...],
* 'content' => ['message_text' => 'Hello', ...],
* 'styles' => ['background_color' => '#000', ...],
* 'general' => ['priority' => 10, ...]
* ]
* ```
*
* @param array $groupedConfig Configuración ya agrupada
* @return self
*/
public static function fromArray(array $groupedConfig): self
{
// Si ya está agrupado, usarlo directamente
return new self($groupedConfig);
}
/**
* Inferir grupo desde clave (heurística)
*
* @param string $key
* @return string
*/
private static function inferGroup(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';
}
/**
* Crear configuración vacía
*
* @return self
*/
public static function empty(): self
{
return new self([
'visibility' => [],
'content' => [],
'styles' => [],
'general' => []
]);
}
}