Files
roi-theme/Shared/Domain/ValueObjects/ComponentContent.php
FrankZamora 90863cd8f5 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>
2025-11-26 22:53:34 -06:00

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;
}
}