- Create PostGridShortcodeRegistrar for WordPress shortcode registration - Implement RenderPostGridUseCase following Clean Architecture - Add PostGridQueryBuilder for custom WP_Query construction - Add PostGridShortcodeRenderer for HTML/CSS generation - Register shortcode in DIContainer with proper DI - Add shortcode usage guide in post-grid admin panel - Fix sidebar layout: add hide_for_logged_in check to wrapper visibility Shortcode attributes: category, tag, author, posts_per_page, columns, show_pagination, show_thumbnail, show_excerpt, show_meta, etc. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
546 lines
19 KiB
PHP
546 lines
19 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace ROITheme\Shared\Infrastructure\Di;
|
|
|
|
use ROITheme\Shared\Domain\Contracts\ComponentRepositoryInterface;
|
|
use ROITheme\Shared\Domain\Contracts\ComponentDefaultsRepositoryInterface;
|
|
use ROITheme\Shared\Domain\Contracts\ValidationServiceInterface;
|
|
use ROITheme\Shared\Domain\Contracts\CacheServiceInterface;
|
|
use ROITheme\Shared\Domain\Contracts\CSSGeneratorInterface;
|
|
use ROITheme\Shared\Domain\Contracts\ComponentSettingsRepositoryInterface;
|
|
use ROITheme\Shared\Domain\Contracts\CriticalCSSCollectorInterface;
|
|
use ROITheme\Shared\Infrastructure\Persistence\WordPress\WordPressComponentRepository;
|
|
use ROITheme\Shared\Infrastructure\Persistence\WordPress\WordPressDefaultsRepository;
|
|
use ROITheme\Shared\Infrastructure\Persistence\WordPress\WordPressComponentSettingsRepository;
|
|
use ROITheme\Shared\Infrastructure\Services\WordPressValidationService;
|
|
use ROITheme\Shared\Infrastructure\Services\WordPressCacheService;
|
|
use ROITheme\Shared\Infrastructure\Services\SchemaSyncService;
|
|
use ROITheme\Shared\Infrastructure\Services\CleanupService;
|
|
use ROITheme\Shared\Infrastructure\Services\CSSGeneratorService;
|
|
use ROITheme\Shared\Infrastructure\Services\CriticalCSSCollector;
|
|
use ROITheme\Shared\Application\UseCases\GetComponentSettings\GetComponentSettingsUseCase;
|
|
use ROITheme\Shared\Application\UseCases\SaveComponentSettings\SaveComponentSettingsUseCase;
|
|
use ROITheme\Public\AdsensePlacement\Infrastructure\Ui\AdsensePlacementRenderer;
|
|
use ROITheme\Shared\Domain\Contracts\PageVisibilityRepositoryInterface;
|
|
use ROITheme\Shared\Domain\Contracts\PageTypeDetectorInterface;
|
|
use ROITheme\Shared\Infrastructure\Services\WordPressPageTypeDetector;
|
|
use ROITheme\Shared\Infrastructure\Persistence\WordPress\WordPressPageVisibilityRepository;
|
|
use ROITheme\Shared\Application\UseCases\EvaluatePageVisibility\EvaluatePageVisibilityUseCase;
|
|
use ROITheme\Shared\Infrastructure\Services\MigratePageVisibilityService;
|
|
// Exclusion System (Plan 99.11)
|
|
use ROITheme\Shared\Domain\Contracts\ExclusionRepositoryInterface;
|
|
use ROITheme\Shared\Domain\Contracts\PageContextProviderInterface;
|
|
use ROITheme\Shared\Domain\Contracts\ServerRequestProviderInterface;
|
|
use ROITheme\Shared\Infrastructure\Persistence\WordPress\WordPressExclusionRepository;
|
|
use ROITheme\Shared\Infrastructure\Services\WordPressPageContextProvider;
|
|
use ROITheme\Shared\Infrastructure\Services\WordPressServerRequestProvider;
|
|
use ROITheme\Shared\Application\UseCases\EvaluateExclusions\EvaluateExclusionsUseCase;
|
|
use ROITheme\Shared\Application\UseCases\EvaluateComponentVisibility\EvaluateComponentVisibilityUseCase;
|
|
// Wrapper Visibility System (Plan 99.15)
|
|
use ROITheme\Shared\Domain\Contracts\WrapperVisibilityCheckerInterface;
|
|
use ROITheme\Shared\Infrastructure\Persistence\WordPress\WordPressComponentVisibilityRepository;
|
|
use ROITheme\Shared\Application\UseCases\CheckWrapperVisibilityUseCase;
|
|
use ROITheme\Shared\Infrastructure\Wordpress\BodyClassHooksRegistrar;
|
|
// Post Grid Shortcode (Plan post-grid-shortcode)
|
|
use ROITheme\Shared\Domain\Contracts\PostGridQueryBuilderInterface;
|
|
use ROITheme\Shared\Domain\Contracts\PostGridShortcodeRendererInterface;
|
|
use ROITheme\Shared\Infrastructure\Query\PostGridQueryBuilder;
|
|
use ROITheme\Shared\Infrastructure\Ui\PostGridShortcodeRenderer;
|
|
use ROITheme\Shared\Application\UseCases\RenderPostGrid\RenderPostGridUseCase;
|
|
|
|
/**
|
|
* DIContainer - Contenedor de Inyección de Dependencias
|
|
*
|
|
* RESPONSABILIDAD: Crear y gestionar instancias de servicios
|
|
*
|
|
* PATRÓN: Service Locator + Lazy Initialization
|
|
* - Lazy Initialization: Crear instancias solo cuando se necesitan
|
|
* - Singleton Pattern: Una sola instancia por servicio
|
|
* - Dependency Resolution: Resolver dependencias automáticamente
|
|
*
|
|
* USO:
|
|
* ```php
|
|
* $container = new DIContainer($wpdb, '/path/to/schemas');
|
|
* $repository = $container->getComponentRepository();
|
|
* $service = $container->getValidationService();
|
|
* ```
|
|
*
|
|
* @package ROITheme\Shared\Infrastructure\DI
|
|
*/
|
|
final class DIContainer
|
|
{
|
|
private array $instances = [];
|
|
|
|
/**
|
|
* Instancia singleton del contenedor
|
|
* @var self|null
|
|
*/
|
|
private static ?self $instance = null;
|
|
|
|
/**
|
|
* Obtiene la instancia singleton del contenedor
|
|
*
|
|
* NOTA: Se debe haber creado una instancia previamente en functions.php
|
|
* El constructor registra automáticamente la instancia.
|
|
*
|
|
* @return self
|
|
* @throws \RuntimeException Si no se ha inicializado el contenedor
|
|
*/
|
|
public static function getInstance(): self
|
|
{
|
|
if (self::$instance === null) {
|
|
throw new \RuntimeException(
|
|
'DIContainer no ha sido inicializado. Asegúrate de que functions.php se haya ejecutado primero.'
|
|
);
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
public function __construct(
|
|
private \wpdb $wpdb,
|
|
private string $schemasPath
|
|
) {
|
|
// Registrar como instancia singleton
|
|
self::$instance = $this;
|
|
}
|
|
|
|
/**
|
|
* Obtener repositorio de componentes
|
|
*
|
|
* Lazy initialization: Crea la instancia solo en la primera llamada
|
|
*
|
|
* @return ComponentRepositoryInterface
|
|
*/
|
|
public function getComponentRepository(): ComponentRepositoryInterface
|
|
{
|
|
if (!isset($this->instances['componentRepository'])) {
|
|
$this->instances['componentRepository'] = new WordPressComponentRepository(
|
|
$this->wpdb
|
|
);
|
|
}
|
|
|
|
return $this->instances['componentRepository'];
|
|
}
|
|
|
|
/**
|
|
* Obtener repositorio de defaults
|
|
*
|
|
* Lazy initialization: Crea la instancia solo en la primera llamada
|
|
*
|
|
* @return ComponentDefaultsRepositoryInterface
|
|
*/
|
|
public function getDefaultsRepository(): ComponentDefaultsRepositoryInterface
|
|
{
|
|
if (!isset($this->instances['defaultsRepository'])) {
|
|
$this->instances['defaultsRepository'] = new WordPressDefaultsRepository(
|
|
$this->wpdb
|
|
);
|
|
}
|
|
|
|
return $this->instances['defaultsRepository'];
|
|
}
|
|
|
|
/**
|
|
* Obtener servicio de validación
|
|
*
|
|
* Lazy initialization: Crea la instancia solo en la primera llamada
|
|
* Resuelve dependencia: getDefaultsRepository()
|
|
*
|
|
* @return ValidationServiceInterface
|
|
*/
|
|
public function getValidationService(): ValidationServiceInterface
|
|
{
|
|
if (!isset($this->instances['validationService'])) {
|
|
$this->instances['validationService'] = new WordPressValidationService(
|
|
$this->getDefaultsRepository()
|
|
);
|
|
}
|
|
|
|
return $this->instances['validationService'];
|
|
}
|
|
|
|
/**
|
|
* Obtener servicio de cache
|
|
*
|
|
* Lazy initialization: Crea la instancia solo en la primera llamada
|
|
*
|
|
* @return CacheServiceInterface
|
|
*/
|
|
public function getCacheService(): CacheServiceInterface
|
|
{
|
|
if (!isset($this->instances['cacheService'])) {
|
|
$this->instances['cacheService'] = new WordPressCacheService(
|
|
$this->wpdb
|
|
);
|
|
}
|
|
|
|
return $this->instances['cacheService'];
|
|
}
|
|
|
|
/**
|
|
* Obtener servicio de sincronización de schemas
|
|
*
|
|
* Lazy initialization: Crea la instancia solo en la primera llamada
|
|
* Resuelve dependencia: getDefaultsRepository()
|
|
*
|
|
* @return SchemaSyncService
|
|
*/
|
|
public function getSchemaSyncService(): SchemaSyncService
|
|
{
|
|
if (!isset($this->instances['schemaSyncService'])) {
|
|
$this->instances['schemaSyncService'] = new SchemaSyncService(
|
|
$this->getDefaultsRepository(),
|
|
$this->schemasPath
|
|
);
|
|
}
|
|
|
|
return $this->instances['schemaSyncService'];
|
|
}
|
|
|
|
/**
|
|
* Obtener servicio de limpieza
|
|
*
|
|
* Lazy initialization: Crea la instancia solo en la primera llamada
|
|
* Resuelve dependencias: getComponentRepository(), getDefaultsRepository()
|
|
*
|
|
* @return CleanupService
|
|
*/
|
|
public function getCleanupService(): CleanupService
|
|
{
|
|
if (!isset($this->instances['cleanupService'])) {
|
|
$this->instances['cleanupService'] = new CleanupService(
|
|
$this->getComponentRepository(),
|
|
$this->getDefaultsRepository()
|
|
);
|
|
}
|
|
|
|
return $this->instances['cleanupService'];
|
|
}
|
|
|
|
/**
|
|
* Obtener servicio de generación de CSS
|
|
*
|
|
* Lazy initialization: Crea la instancia solo en la primera llamada
|
|
* Sin dependencias
|
|
*
|
|
* @return CSSGeneratorInterface
|
|
*/
|
|
public function getCSSGeneratorService(): CSSGeneratorInterface
|
|
{
|
|
if (!isset($this->instances['cssGeneratorService'])) {
|
|
$this->instances['cssGeneratorService'] = new CSSGeneratorService();
|
|
}
|
|
|
|
return $this->instances['cssGeneratorService'];
|
|
}
|
|
|
|
/**
|
|
* Obtener repositorio de configuraciones de componentes
|
|
*
|
|
* Lazy initialization: Crea la instancia solo en la primera llamada
|
|
*
|
|
* @return ComponentSettingsRepositoryInterface
|
|
*/
|
|
public function getComponentSettingsRepository(): ComponentSettingsRepositoryInterface
|
|
{
|
|
if (!isset($this->instances['componentSettingsRepository'])) {
|
|
$this->instances['componentSettingsRepository'] = new WordPressComponentSettingsRepository(
|
|
$this->wpdb
|
|
);
|
|
}
|
|
|
|
return $this->instances['componentSettingsRepository'];
|
|
}
|
|
|
|
/**
|
|
* Obtener caso de uso para obtener configuraciones de componentes
|
|
*
|
|
* Lazy initialization: Crea la instancia solo en la primera llamada
|
|
* Resuelve dependencia: getComponentSettingsRepository()
|
|
*
|
|
* @return GetComponentSettingsUseCase
|
|
*/
|
|
public function getGetComponentSettingsUseCase(): GetComponentSettingsUseCase
|
|
{
|
|
if (!isset($this->instances['getComponentSettingsUseCase'])) {
|
|
$this->instances['getComponentSettingsUseCase'] = new GetComponentSettingsUseCase(
|
|
$this->getComponentSettingsRepository()
|
|
);
|
|
}
|
|
|
|
return $this->instances['getComponentSettingsUseCase'];
|
|
}
|
|
|
|
/**
|
|
* Obtener caso de uso para guardar configuraciones de componentes
|
|
*
|
|
* Lazy initialization: Crea la instancia solo en la primera llamada
|
|
* Resuelve dependencia: getComponentSettingsRepository()
|
|
*
|
|
* @return SaveComponentSettingsUseCase
|
|
*/
|
|
public function getSaveComponentSettingsUseCase(): SaveComponentSettingsUseCase
|
|
{
|
|
if (!isset($this->instances['saveComponentSettingsUseCase'])) {
|
|
$this->instances['saveComponentSettingsUseCase'] = new SaveComponentSettingsUseCase(
|
|
$this->getComponentSettingsRepository()
|
|
);
|
|
}
|
|
|
|
return $this->instances['saveComponentSettingsUseCase'];
|
|
}
|
|
|
|
/**
|
|
* Obtener renderer de AdSense Placement
|
|
*
|
|
* Lazy initialization: Crea la instancia solo en la primera llamada
|
|
* Resuelve dependencia: getCSSGeneratorService()
|
|
*
|
|
* @return AdsensePlacementRenderer
|
|
*/
|
|
public function getAdsensePlacementRenderer(): AdsensePlacementRenderer
|
|
{
|
|
if (!isset($this->instances['adsensePlacementRenderer'])) {
|
|
$this->instances['adsensePlacementRenderer'] = new AdsensePlacementRenderer(
|
|
$this->getCSSGeneratorService()
|
|
);
|
|
}
|
|
|
|
return $this->instances['adsensePlacementRenderer'];
|
|
}
|
|
|
|
/**
|
|
* Obtener colector de CSS crítico
|
|
*
|
|
* Lazy initialization: Crea la instancia solo en la primera llamada
|
|
* IMPORTANTE: Singleton - misma instancia para todos los Renderers
|
|
*
|
|
* @return CriticalCSSCollectorInterface
|
|
*/
|
|
public function getCriticalCSSCollector(): CriticalCSSCollectorInterface
|
|
{
|
|
if (!isset($this->instances['criticalCSSCollector'])) {
|
|
$this->instances['criticalCSSCollector'] = new CriticalCSSCollector();
|
|
}
|
|
|
|
return $this->instances['criticalCSSCollector'];
|
|
}
|
|
|
|
// ===============================
|
|
// Page Visibility System
|
|
// ===============================
|
|
|
|
/**
|
|
* Obtiene el repositorio de visibilidad de página
|
|
*
|
|
* IMPORTANTE: Inyecta $wpdb para consistencia con el resto del código
|
|
* (WordPressComponentSettingsRepository también recibe $wpdb por constructor)
|
|
*/
|
|
public function getPageVisibilityRepository(): PageVisibilityRepositoryInterface
|
|
{
|
|
if (!isset($this->instances['pageVisibilityRepository'])) {
|
|
// Inyectar $wpdb siguiendo el patrón existente
|
|
$this->instances['pageVisibilityRepository'] = new WordPressPageVisibilityRepository($this->wpdb);
|
|
}
|
|
return $this->instances['pageVisibilityRepository'];
|
|
}
|
|
|
|
/**
|
|
* Obtiene el detector de tipo de página
|
|
*/
|
|
public function getPageTypeDetector(): PageTypeDetectorInterface
|
|
{
|
|
if (!isset($this->instances['pageTypeDetector'])) {
|
|
$this->instances['pageTypeDetector'] = new WordPressPageTypeDetector();
|
|
}
|
|
return $this->instances['pageTypeDetector'];
|
|
}
|
|
|
|
/**
|
|
* Obtiene el caso de uso de evaluación de visibilidad
|
|
*/
|
|
public function getEvaluatePageVisibilityUseCase(): EvaluatePageVisibilityUseCase
|
|
{
|
|
if (!isset($this->instances['evaluatePageVisibilityUseCase'])) {
|
|
$this->instances['evaluatePageVisibilityUseCase'] = new EvaluatePageVisibilityUseCase(
|
|
$this->getPageTypeDetector(),
|
|
$this->getPageVisibilityRepository()
|
|
);
|
|
}
|
|
return $this->instances['evaluatePageVisibilityUseCase'];
|
|
}
|
|
|
|
/**
|
|
* Obtiene el servicio de migración de visibilidad
|
|
*/
|
|
public function getMigratePageVisibilityService(): MigratePageVisibilityService
|
|
{
|
|
if (!isset($this->instances['migratePageVisibilityService'])) {
|
|
$this->instances['migratePageVisibilityService'] = new MigratePageVisibilityService(
|
|
$this->getPageVisibilityRepository()
|
|
);
|
|
}
|
|
return $this->instances['migratePageVisibilityService'];
|
|
}
|
|
|
|
// ===============================
|
|
// Exclusion System (Plan 99.11)
|
|
// ===============================
|
|
|
|
/**
|
|
* Obtiene el proveedor de request HTTP
|
|
*
|
|
* Encapsula acceso a $_SERVER
|
|
*/
|
|
public function getServerRequestProvider(): ServerRequestProviderInterface
|
|
{
|
|
if (!isset($this->instances['serverRequestProvider'])) {
|
|
$this->instances['serverRequestProvider'] = new WordPressServerRequestProvider();
|
|
}
|
|
return $this->instances['serverRequestProvider'];
|
|
}
|
|
|
|
/**
|
|
* Obtiene el repositorio de exclusiones
|
|
*/
|
|
public function getExclusionRepository(): ExclusionRepositoryInterface
|
|
{
|
|
if (!isset($this->instances['exclusionRepository'])) {
|
|
$this->instances['exclusionRepository'] = new WordPressExclusionRepository($this->wpdb);
|
|
}
|
|
return $this->instances['exclusionRepository'];
|
|
}
|
|
|
|
/**
|
|
* Obtiene el proveedor de contexto de página
|
|
*/
|
|
public function getPageContextProvider(): PageContextProviderInterface
|
|
{
|
|
if (!isset($this->instances['pageContextProvider'])) {
|
|
$this->instances['pageContextProvider'] = new WordPressPageContextProvider(
|
|
$this->getServerRequestProvider()
|
|
);
|
|
}
|
|
return $this->instances['pageContextProvider'];
|
|
}
|
|
|
|
/**
|
|
* Obtiene el caso de uso de evaluación de exclusiones
|
|
*/
|
|
public function getEvaluateExclusionsUseCase(): EvaluateExclusionsUseCase
|
|
{
|
|
if (!isset($this->instances['evaluateExclusionsUseCase'])) {
|
|
$this->instances['evaluateExclusionsUseCase'] = new EvaluateExclusionsUseCase(
|
|
$this->getExclusionRepository(),
|
|
$this->getPageContextProvider()
|
|
);
|
|
}
|
|
return $this->instances['evaluateExclusionsUseCase'];
|
|
}
|
|
|
|
/**
|
|
* Obtiene el caso de uso orquestador de visibilidad completa
|
|
*
|
|
* Combina visibilidad por tipo de página + exclusiones
|
|
*/
|
|
public function getEvaluateComponentVisibilityUseCase(): EvaluateComponentVisibilityUseCase
|
|
{
|
|
if (!isset($this->instances['evaluateComponentVisibilityUseCase'])) {
|
|
$this->instances['evaluateComponentVisibilityUseCase'] = new EvaluateComponentVisibilityUseCase(
|
|
$this->getEvaluatePageVisibilityUseCase(),
|
|
$this->getEvaluateExclusionsUseCase()
|
|
);
|
|
}
|
|
return $this->instances['evaluateComponentVisibilityUseCase'];
|
|
}
|
|
|
|
// ===============================
|
|
// Wrapper Visibility System (Plan 99.15)
|
|
// ===============================
|
|
|
|
/**
|
|
* Obtiene el repositorio de visibilidad de wrappers
|
|
*
|
|
* Implementa WrapperVisibilityCheckerInterface
|
|
*/
|
|
public function getWrapperVisibilityChecker(): WrapperVisibilityCheckerInterface
|
|
{
|
|
if (!isset($this->instances['wrapperVisibilityChecker'])) {
|
|
$this->instances['wrapperVisibilityChecker'] = new WordPressComponentVisibilityRepository($this->wpdb);
|
|
}
|
|
return $this->instances['wrapperVisibilityChecker'];
|
|
}
|
|
|
|
/**
|
|
* Obtiene el caso de uso para verificar visibilidad de wrappers
|
|
*
|
|
* Usado por WrapperVisibilityService para templates
|
|
*/
|
|
public function getCheckWrapperVisibilityUseCase(): CheckWrapperVisibilityUseCase
|
|
{
|
|
if (!isset($this->instances['checkWrapperVisibilityUseCase'])) {
|
|
$this->instances['checkWrapperVisibilityUseCase'] = new CheckWrapperVisibilityUseCase(
|
|
$this->getWrapperVisibilityChecker()
|
|
);
|
|
}
|
|
return $this->instances['checkWrapperVisibilityUseCase'];
|
|
}
|
|
|
|
/**
|
|
* Obtiene el registrador de hooks para body_class
|
|
*
|
|
* CSS failsafe: Agrega clases cuando componentes están ocultos
|
|
*/
|
|
public function getBodyClassHooksRegistrar(): BodyClassHooksRegistrar
|
|
{
|
|
if (!isset($this->instances['bodyClassHooksRegistrar'])) {
|
|
$this->instances['bodyClassHooksRegistrar'] = new BodyClassHooksRegistrar();
|
|
}
|
|
return $this->instances['bodyClassHooksRegistrar'];
|
|
}
|
|
|
|
// ===============================
|
|
// Post Grid Shortcode System
|
|
// ===============================
|
|
|
|
/**
|
|
* Obtiene el query builder para post grid shortcode
|
|
*/
|
|
public function getPostGridQueryBuilder(): PostGridQueryBuilderInterface
|
|
{
|
|
if (!isset($this->instances['postGridQueryBuilder'])) {
|
|
$this->instances['postGridQueryBuilder'] = new PostGridQueryBuilder();
|
|
}
|
|
return $this->instances['postGridQueryBuilder'];
|
|
}
|
|
|
|
/**
|
|
* Obtiene el renderer para post grid shortcode
|
|
*/
|
|
public function getPostGridShortcodeRenderer(): PostGridShortcodeRendererInterface
|
|
{
|
|
if (!isset($this->instances['postGridShortcodeRenderer'])) {
|
|
$this->instances['postGridShortcodeRenderer'] = new PostGridShortcodeRenderer(
|
|
$this->getCSSGeneratorService()
|
|
);
|
|
}
|
|
return $this->instances['postGridShortcodeRenderer'];
|
|
}
|
|
|
|
/**
|
|
* Obtiene el caso de uso para renderizar post grid shortcode
|
|
*/
|
|
public function getRenderPostGridUseCase(): RenderPostGridUseCase
|
|
{
|
|
if (!isset($this->instances['renderPostGridUseCase'])) {
|
|
$this->instances['renderPostGridUseCase'] = new RenderPostGridUseCase(
|
|
$this->getPostGridQueryBuilder(),
|
|
$this->getPostGridShortcodeRenderer(),
|
|
$this->getComponentSettingsRepository()
|
|
);
|
|
}
|
|
return $this->instances['renderPostGridUseCase'];
|
|
}
|
|
}
|