value(); // "top_bar" * echo $name->toString(); // "top_bar" * ``` * * @package ROITheme\Shared\Domain\ValueObjects */ final readonly class ComponentName { /** * Longitud mínima del nombre */ private const MIN_LENGTH = 3; /** * Longitud máxima del nombre */ private const MAX_LENGTH = 50; /** * Patrón regex para validar formato del nombre * - Debe comenzar con letra minúscula * - Puede contener letras minúsculas, números, guiones bajos y guiones */ private const PATTERN = '/^[a-z][a-z0-9_-]*$/'; /** * @param string $value Nombre del componente * @throws InvalidComponentException Si el nombre no cumple las reglas de negocio */ public function __construct(private string $value) { $this->validate(); } /** * Obtener el valor del nombre * * @return string */ public function value(): string { return $this->value; } /** * Convertir a string * * @return string */ public function toString(): string { return $this->value; } /** * Comparar con otro ComponentName * * @param ComponentName $other * @return bool */ public function equals(ComponentName $other): bool { return $this->value === $other->value; } /** * Validar reglas de negocio del nombre * * @throws InvalidComponentException * @return void */ private function validate(): void { // Regla 1: No puede estar vacío if (empty($this->value)) { throw new InvalidComponentException('Component name cannot be empty'); } // Regla 2: Longitud entre MIN y MAX $length = strlen($this->value); if ($length < self::MIN_LENGTH || $length > self::MAX_LENGTH) { throw new InvalidComponentException( sprintf( 'Component name must be between %d and %d characters (got %d)', self::MIN_LENGTH, self::MAX_LENGTH, $length ) ); } // Regla 3: Formato válido (lowercase, números, guiones bajos, comienza con letra) if (!preg_match(self::PATTERN, $this->value)) { throw new InvalidComponentException( 'Component name must start with a letter and contain only lowercase letters, numbers, underscores, and hyphens' ); } } /** * Crear desde string (factory method) * * @param string $value * @return self */ public static function fromString(string $value): self { return new self($value); } /** * Normalizar string a formato de nombre válido * * Convierte: * - "Top Bar" → "top_bar" * - "FOOTER-CTA" → "footer_cta" * - " sidebar " → "sidebar" * * @param string $value * @return self */ public static function fromNormalized(string $value): self { // Trim espacios $normalized = trim($value); // Convertir a minúsculas $normalized = strtolower($normalized); // Reemplazar espacios y guiones por guiones bajos $normalized = str_replace([' ', '-'], '_', $normalized); // Eliminar caracteres no permitidos $normalized = preg_replace('/[^a-z0-9_]/', '', $normalized); // Eliminar guiones bajos duplicados $normalized = preg_replace('/_+/', '_', $normalized); // Eliminar guiones bajos al inicio/final $normalized = trim($normalized, '_'); return new self($normalized); } }