- eliminar carpetas vacias admin/herosection y bootstrapicons - eliminar 7 scripts legacy con credenciales hardcodeadas - eliminar formbuilder duplicado en shared/infrastructure/ui - eliminar 11 archivos .gitkeep en carpetas con contenido 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
125 lines
3.3 KiB
PHP
125 lines
3.3 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace ROITheme\Admin\CustomCSSManager\Domain\ValueObjects;
|
|
|
|
use ROITheme\Shared\Domain\Exceptions\ValidationException;
|
|
|
|
/**
|
|
* Value Object para ID único de snippet CSS
|
|
*
|
|
* Soporta tres formatos:
|
|
* 1. Generado: css_[timestamp]_[random] (ej: "css_1701432000_a1b2c3")
|
|
* 2. Legacy con prefijo: css_[descriptive]_[number] (ej: "css_tablas_apu_1764624826")
|
|
* 3. Legacy kebab-case: (ej: "cls-tables-apu", "generic-tables")
|
|
*
|
|
* Esto permite migrar snippets existentes sin romper IDs.
|
|
*/
|
|
final class SnippetId
|
|
{
|
|
private const PREFIX = 'css_';
|
|
private const PATTERN_GENERATED = '/^css_[0-9]+_[a-z0-9]{6}$/';
|
|
private const PATTERN_LEGACY_PREFIX = '/^css_[a-z0-9_]+$/';
|
|
private const PATTERN_LEGACY = '/^[a-z0-9]+(-[a-z0-9]+)*$/';
|
|
|
|
private function __construct(
|
|
private readonly string $value
|
|
) {}
|
|
|
|
/**
|
|
* Crea SnippetId desde string existente (desde BD)
|
|
*
|
|
* Acepta tanto IDs generados (css_*) como IDs legacy (kebab-case).
|
|
*
|
|
* @param string $id ID existente
|
|
* @return self
|
|
* @throws ValidationException Si el formato es inválido
|
|
*/
|
|
public static function fromString(string $id): self
|
|
{
|
|
$id = trim($id);
|
|
|
|
if (empty($id)) {
|
|
throw new ValidationException('El ID del snippet no puede estar vacío');
|
|
}
|
|
|
|
if (strlen($id) > 50) {
|
|
throw new ValidationException('El ID del snippet no puede exceder 50 caracteres');
|
|
}
|
|
|
|
// Validar formato generado (css_*)
|
|
if (str_starts_with($id, self::PREFIX)) {
|
|
// Acepta formato nuevo (css_timestamp_random) o legacy (css_descriptivo_numero)
|
|
if (!preg_match(self::PATTERN_GENERATED, $id) && !preg_match(self::PATTERN_LEGACY_PREFIX, $id)) {
|
|
throw new ValidationException(
|
|
sprintf('Formato de ID generado inválido: %s. Esperado: css_[timestamp]_[random]', $id)
|
|
);
|
|
}
|
|
return new self($id);
|
|
}
|
|
|
|
// Validar formato legacy (kebab-case)
|
|
if (!preg_match(self::PATTERN_LEGACY, $id)) {
|
|
throw new ValidationException(
|
|
sprintf('Formato de ID inválido: %s. Use kebab-case (ej: cls-tables-apu)', $id)
|
|
);
|
|
}
|
|
|
|
return new self($id);
|
|
}
|
|
|
|
/**
|
|
* Genera un nuevo SnippetId único
|
|
*
|
|
* @return self
|
|
*/
|
|
public static function generate(): self
|
|
{
|
|
$timestamp = time();
|
|
$random = bin2hex(random_bytes(3));
|
|
|
|
return new self(self::PREFIX . $timestamp . '_' . $random);
|
|
}
|
|
|
|
/**
|
|
* Verifica si es un ID generado (vs legacy)
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function isGenerated(): bool
|
|
{
|
|
return str_starts_with($this->value, self::PREFIX);
|
|
}
|
|
|
|
/**
|
|
* Obtiene el valor del ID
|
|
*
|
|
* @return string
|
|
*/
|
|
public function value(): string
|
|
{
|
|
return $this->value;
|
|
}
|
|
|
|
/**
|
|
* Compara igualdad con otro SnippetId
|
|
*
|
|
* @param SnippetId $other
|
|
* @return bool
|
|
*/
|
|
public function equals(SnippetId $other): bool
|
|
{
|
|
return $this->value === $other->value;
|
|
}
|
|
|
|
/**
|
|
* Representación string
|
|
*
|
|
* @return string
|
|
*/
|
|
public function __toString(): string
|
|
{
|
|
return $this->value;
|
|
}
|
|
}
|