fix(wrappers): eliminar wrappers vacíos y corregir exclusiones AdSense (Plan 99.15)

## Problema
- Componentes deshabilitados/excluidos dejaban wrappers HTML vacíos
  (navbar 32px, sidebar col-lg-3 294px)
- AdSense ignoraba exclusiones por URL pattern en grupo _exclusions

## Solución Plan 99.15 (Clean Architecture)

### Domain Layer
- WrapperVisibilityCheckerInterface: contrato para verificar visibilidad

### Application Layer
- CheckWrapperVisibilityUseCase: orquesta verificaciones de visibilidad

### Infrastructure Layer
- WordPressComponentVisibilityRepository: consulta BD + PageVisibilityHelper
- WrapperVisibilityService: facade estático para templates
- BodyClassHooksRegistrar: agrega clases CSS failsafe al body

### Templates modificados
- header.php: renderizado condicional de <nav> wrapper
- page.php/single.php: lógica dinámica col-lg-9/col-lg-12 según sidebar

### CSS Failsafe
- css-global-utilities.css: reglas body.roi-hide-* como respaldo

## Fix AdSense (Inc/adsense-placement.php)
- Agregado PageVisibilityHelper::shouldShow() a todas las funciones:
  roi_render_ad_slot, roi_render_rail_ads, roi_enqueue_adsense_script,
  roi_inject_content_ads, roi_render_anchor_ads, roi_render_vignette_ad,
  roi_enqueue_anchor_vignette_scripts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
FrankZamora
2025-12-04 11:46:21 -06:00
parent 23339e3349
commit 36d5cf56de
12 changed files with 617 additions and 12 deletions

View File

@@ -0,0 +1,100 @@
<?php
declare(strict_types=1);
namespace ROITheme\Shared\Infrastructure\Services;
use ROITheme\Shared\Application\UseCases\CheckWrapperVisibilityUseCase;
use ROITheme\Shared\Infrastructure\Di\DIContainer;
/**
* Servicio facade para verificar visibilidad de wrappers desde templates
*
* Responsabilidad: Proveer acceso simplificado (singleton/static) al
* CheckWrapperVisibilityUseCase para uso en templates PHP.
*
* USO EN TEMPLATES:
* ```php
* if (WrapperVisibilityService::shouldRenderWrapper('navbar')) {
* // Renderizar wrapper y componente
* }
* ```
*
* @package ROITheme\Shared\Infrastructure\Services
* @see Plan 99.15 - Fix Empty Layout Wrappers
*/
final class WrapperVisibilityService
{
private static ?CheckWrapperVisibilityUseCase $useCase = null;
/**
* Verifica si el wrapper de un componente debe renderizarse
*
* @param string $componentName Nombre del componente (kebab-case)
* @return bool True si el wrapper debe renderizarse
*/
public static function shouldRenderWrapper(string $componentName): bool
{
$useCase = self::getUseCase();
$isMobile = self::detectMobile();
return $useCase->execute($componentName, $isMobile);
}
/**
* Verifica visibilidad para múltiples componentes
*
* Útil para determinar si renderizar un contenedor que agrupa varios componentes
*
* @param array<string> $componentNames Lista de nombres de componentes
* @return bool True si AL MENOS UNO de los componentes debe mostrarse
*/
public static function shouldRenderAnyWrapper(array $componentNames): bool
{
foreach ($componentNames as $componentName) {
if (self::shouldRenderWrapper($componentName)) {
return true;
}
}
return false;
}
/**
* Obtiene o crea el UseCase
*
* @return CheckWrapperVisibilityUseCase
*/
private static function getUseCase(): CheckWrapperVisibilityUseCase
{
if (self::$useCase === null) {
$container = DIContainer::getInstance();
self::$useCase = $container->getCheckWrapperVisibilityUseCase();
}
return self::$useCase;
}
/**
* Detecta si el dispositivo actual es móvil
*
* Usa wp_is_mobile() de WordPress
*
* @return bool
*/
private static function detectMobile(): bool
{
if (function_exists('wp_is_mobile')) {
return wp_is_mobile();
}
return false;
}
/**
* Limpia la instancia del UseCase (útil para tests)
*/
public static function reset(): void
{
self::$useCase = null;
}
}