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>
273 lines
6.4 KiB
PHP
273 lines
6.4 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace ROITheme\Shared\Domain\ValueObjects;
|
|
|
|
use ROITheme\Shared\Domain\Exceptions\InvalidComponentException;
|
|
|
|
/**
|
|
* ComponentContent - Value Object inmutable para contenido de componente
|
|
*
|
|
* RESPONSABILIDAD: Representar y validar el contenido textual y de CTA de un componente
|
|
*
|
|
* REGLAS DE NEGOCIO:
|
|
* - El mensaje de texto puede estar vacío (componentes sin texto)
|
|
* - Si existe CTA URL debe existir CTA text (y viceversa)
|
|
* - CTA URL debe ser una URL válida (formato http/https)
|
|
* - CTA text no puede estar vacío si existe CTA URL
|
|
* - Title es opcional
|
|
*
|
|
* CASOS DE USO:
|
|
* - Componente solo con mensaje de texto
|
|
* - Componente con mensaje y CTA
|
|
* - Componente con title, mensaje y CTA
|
|
* - Componente sin contenido textual (solo estilos/visibilidad)
|
|
*
|
|
* USO:
|
|
* ```php
|
|
* // Componente con mensaje y CTA
|
|
* $content = new ComponentContent(
|
|
* messageText: 'Welcome to our site!',
|
|
* ctaText: 'Learn More',
|
|
* ctaUrl: 'https://example.com/about'
|
|
* );
|
|
*
|
|
* // Componente solo con mensaje
|
|
* $content = ComponentContent::messageOnly('Hello World!');
|
|
*
|
|
* // Componente vacío
|
|
* $content = ComponentContent::empty();
|
|
* ```
|
|
*
|
|
* @package ROITheme\Shared\Domain\ValueObjects
|
|
*/
|
|
final readonly class ComponentContent
|
|
{
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param string $messageText Texto del mensaje principal
|
|
* @param string|null $ctaText Texto del Call-to-Action
|
|
* @param string|null $ctaUrl URL del Call-to-Action
|
|
* @param string|null $title Título del componente
|
|
* @throws InvalidComponentException
|
|
*/
|
|
public function __construct(
|
|
private string $messageText = '',
|
|
private ?string $ctaText = null,
|
|
private ?string $ctaUrl = null,
|
|
private ?string $title = null
|
|
) {
|
|
$this->validate();
|
|
}
|
|
|
|
/**
|
|
* Obtener mensaje de texto
|
|
*
|
|
* @return string
|
|
*/
|
|
public function messageText(): string
|
|
{
|
|
return $this->messageText;
|
|
}
|
|
|
|
/**
|
|
* Obtener texto del CTA
|
|
*
|
|
* @return string|null
|
|
*/
|
|
public function ctaText(): ?string
|
|
{
|
|
return $this->ctaText;
|
|
}
|
|
|
|
/**
|
|
* Obtener URL del CTA
|
|
*
|
|
* @return string|null
|
|
*/
|
|
public function ctaUrl(): ?string
|
|
{
|
|
return $this->ctaUrl;
|
|
}
|
|
|
|
/**
|
|
* Obtener título
|
|
*
|
|
* @return string|null
|
|
*/
|
|
public function title(): ?string
|
|
{
|
|
return $this->title;
|
|
}
|
|
|
|
/**
|
|
* Verificar si tiene mensaje de texto
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function hasMessage(): bool
|
|
{
|
|
return !empty($this->messageText);
|
|
}
|
|
|
|
/**
|
|
* Verificar si tiene CTA
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function hasCTA(): bool
|
|
{
|
|
return !empty($this->ctaText) && !empty($this->ctaUrl);
|
|
}
|
|
|
|
/**
|
|
* Verificar si tiene título
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function hasTitle(): bool
|
|
{
|
|
return !empty($this->title);
|
|
}
|
|
|
|
/**
|
|
* Verificar si está completamente vacío
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function isEmpty(): bool
|
|
{
|
|
return empty($this->messageText) &&
|
|
empty($this->ctaText) &&
|
|
empty($this->ctaUrl) &&
|
|
empty($this->title);
|
|
}
|
|
|
|
/**
|
|
* Convertir a array
|
|
*
|
|
* @return array
|
|
*/
|
|
public function toArray(): array
|
|
{
|
|
return array_filter([
|
|
'message_text' => $this->messageText,
|
|
'cta_text' => $this->ctaText,
|
|
'cta_url' => $this->ctaUrl,
|
|
'title' => $this->title
|
|
], fn($value) => $value !== null && $value !== '');
|
|
}
|
|
|
|
/**
|
|
* Validar reglas de negocio
|
|
*
|
|
* @throws InvalidComponentException
|
|
* @return void
|
|
*/
|
|
private function validate(): void
|
|
{
|
|
// Regla 1: Si existe CTA URL debe existir CTA text
|
|
if (!empty($this->ctaUrl) && empty($this->ctaText)) {
|
|
throw new InvalidComponentException('CTA URL requires CTA text');
|
|
}
|
|
|
|
// Regla 2: Si existe CTA text debe existir CTA URL
|
|
if (!empty($this->ctaText) && empty($this->ctaUrl)) {
|
|
throw new InvalidComponentException('CTA text requires CTA URL');
|
|
}
|
|
|
|
// Regla 3: CTA URL debe ser URL válida
|
|
if (!empty($this->ctaUrl) && !$this->isValidUrl($this->ctaUrl)) {
|
|
throw new InvalidComponentException(
|
|
sprintf('Invalid CTA URL format: %s', $this->ctaUrl)
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validar si una string es URL válida
|
|
*
|
|
* @param string $url
|
|
* @return bool
|
|
*/
|
|
private function isValidUrl(string $url): bool
|
|
{
|
|
// Debe comenzar con http:// o https://
|
|
if (!preg_match('/^https?:\/\//i', $url)) {
|
|
return false;
|
|
}
|
|
|
|
// Usar filter_var para validación completa
|
|
return filter_var($url, FILTER_VALIDATE_URL) !== false;
|
|
}
|
|
|
|
/**
|
|
* Factory: Contenido vacío
|
|
*
|
|
* @return self
|
|
*/
|
|
public static function empty(): self
|
|
{
|
|
return new self();
|
|
}
|
|
|
|
/**
|
|
* Factory: Solo mensaje
|
|
*
|
|
* @param string $message
|
|
* @return self
|
|
*/
|
|
public static function messageOnly(string $message): self
|
|
{
|
|
return new self(messageText: $message);
|
|
}
|
|
|
|
/**
|
|
* Factory: Mensaje con CTA
|
|
*
|
|
* @param string $message
|
|
* @param string $ctaText
|
|
* @param string $ctaUrl
|
|
* @return self
|
|
*/
|
|
public static function withCTA(string $message, string $ctaText, string $ctaUrl): self
|
|
{
|
|
return new self(
|
|
messageText: $message,
|
|
ctaText: $ctaText,
|
|
ctaUrl: $ctaUrl
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Crear desde array
|
|
*
|
|
* @param array $data
|
|
* @return self
|
|
*/
|
|
public static function fromArray(array $data): self
|
|
{
|
|
return new self(
|
|
messageText: $data['message_text'] ?? '',
|
|
ctaText: $data['cta_text'] ?? null,
|
|
ctaUrl: $data['cta_url'] ?? null,
|
|
title: $data['title'] ?? null
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Comparar con otro ComponentContent
|
|
*
|
|
* @param ComponentContent $other
|
|
* @return bool
|
|
*/
|
|
public function equals(ComponentContent $other): bool
|
|
{
|
|
return $this->messageText === $other->messageText &&
|
|
$this->ctaText === $other->ctaText &&
|
|
$this->ctaUrl === $other->ctaUrl &&
|
|
$this->title === $other->title;
|
|
}
|
|
}
|