feat(exclusions): Implement component exclusion system (Plan 99.11)

Adds ability to exclude components from specific:
- Categories (by slug or term_id)
- Post/Page IDs
- URL patterns (substring or regex)

Architecture:
- Domain: Value Objects (CategoryExclusion, PostIdExclusion,
  UrlPatternExclusion, ExclusionRuleSet) + Contracts
- Application: EvaluateExclusionsUseCase +
  EvaluateComponentVisibilityUseCase (orchestrator)
- Infrastructure: WordPressExclusionRepository,
  WordPressPageContextProvider, WordPressServerRequestProvider
- Admin: ExclusionFormPartial (reusable UI),
  ExclusionFieldProcessor, JS toggle

The PageVisibilityHelper now uses the orchestrator UseCase that
combines page-type visibility (Plan 99.10) with exclusion rules.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
FrankZamora
2025-12-03 10:51:00 -06:00
parent 8735962f52
commit 14138e7762
19 changed files with 1407 additions and 5 deletions

View File

@@ -28,6 +28,15 @@ 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;
/**
* DIContainer - Contenedor de Inyección de Dependencias
@@ -363,4 +372,75 @@ final class DIContainer
}
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'];
}
}