# Plano: Sistema de Plugins ParentEntityHandler ## Problema O módulo structural_pages tem dependências hardcoded para diferentes tipos de entidade pai (taxonomy_term, user, group, node) espalhadas em múltiplos arquivos: - `StructuralPagesSettingsForm.php:46-51` - Array hardcoded `$supportedEntityTypes` - `structural_pages.module:55-59` - `in_array($parent_entity_type, ['user', 'group'])` - `StructuralPagesMenuBlock.php:14` - `use Drupal\group\Entity\GroupInterface;` (causa erro sem o módulo group) - `StructuralPagesMenuBlock.php:274-290` - Verificações `instanceof` para cada tipo - `SectionBreadcrumbBuilder.php:82-100` - Switch statement para tipos de contexto **Consequência:** Adicionar um novo tipo de entidade requer modificar múltiplos arquivos, e o import do GroupInterface causa erro quando o módulo group não está instalado. ## Solução Implementar um sistema de plugins Drupal (Attribute-based para D10+) onde cada handler define: - Tipo de entidade que gerencia - Módulo provedor (para dependências opcionais) - Como detectar a entidade na rota - Como construir breadcrumbs - Se deve limpar `field_site_section` - Campo de ordenação --- ## Nova Estrutura de Arquivos ``` structural_pages/ ├── src/ │ ├── Attribute/ │ │ └── ParentEntityHandler.php # NOVO │ ├── ParentEntityHandler/ │ │ ├── ParentEntityHandlerInterface.php # NOVO │ │ ├── ParentEntityHandlerManagerInterface.php # NOVO │ │ ├── ParentEntityHandlerBase.php # NOVO │ │ └── ParentEntityHandlerManager.php # NOVO │ ├── Plugin/ │ │ ├── Block/ │ │ │ └── StructuralPagesMenuBlock.php # REFATORAR │ │ └── ParentEntityHandler/ # NOVO │ │ ├── TaxonomyTermHandler.php # NOVO │ │ ├── UserHandler.php # NOVO │ │ └── NodeHandler.php # NOVO │ ├── Breadcrumb/ │ │ └── SectionBreadcrumbBuilder.php # REFATORAR │ └── Form/ │ └── StructuralPagesSettingsForm.php # REFATORAR ├── modules/ │ └── structural_pages_group/ # NOVO (submódulo) │ ├── structural_pages_group.info.yml │ └── src/Plugin/ParentEntityHandler/ │ └── GroupHandler.php ├── structural_pages.module # REFATORAR └── structural_pages.services.yml # ATUALIZAR ``` --- ## Componentes Principais ### 1. Attribute (src/Attribute/ParentEntityHandler.php) ```php #[\Attribute(\Attribute::TARGET_CLASS)] class ParentEntityHandler extends Plugin { public function __construct( public readonly string $id, public readonly TranslatableMarkup $label, public readonly string $entity_type_id, public readonly ?string $provider_module = NULL, public readonly bool $clears_site_section = FALSE, public readonly string $sort_field = 'title', public readonly ?string $route_parameter = NULL, public readonly array $bundle_restrictions = [], public readonly int $weight = 0, ) {} } ``` ### 2. Interface (src/ParentEntityHandler/ParentEntityHandlerInterface.php) Métodos principais: - `getEntityTypeId(): string` - `isAvailable(): bool` - `clearsSiteSection(): bool` - `getSortField(): string` - `getEntityFromRoute(RouteMatchInterface): ?EntityInterface` - `handlesEntity(EntityInterface): bool` - `buildBreadcrumb(Breadcrumb, EntityInterface): void` - `getSiteSectionId(EntityInterface): int|string|null` ### 3. Manager (src/ParentEntityHandler/ParentEntityHandlerManager.php) Métodos principais: - `getAvailableHandlers(): array` - Retorna handlers com módulos instalados - `getHandlerForEntityType(string): ?ParentEntityHandlerInterface` - `getSupportedEntityTypes(): array` - Para o formulário de configurações - `clearsSiteSection(string): bool` - `getSortField(string): string` - `getEntityFromRoute(RouteMatchInterface): ?EntityInterface` - `buildBreadcrumbForEntity(Breadcrumb, EntityInterface): bool` ### 4. Handlers Built-in | Handler | entity_type_id | clears_site_section | sort_field | bundle_restrictions | |---------|---------------|---------------------|------------|---------------------| | TaxonomyTermHandler | taxonomy_term | false | name | ['site_sections'] | | UserHandler | user | true | name | [] | | NodeHandler | node | false | title | ['content_page', 'section_page'] | ### 5. Submódulo structural_pages_group ```yaml # structural_pages_group.info.yml name: 'Structural Pages Group Integration' type: module dependencies: - structural_pages:structural_pages - group:group ``` GroupHandler: `entity_type_id: group`, `clears_site_section: true`, `sort_field: label` --- ## Refatorações ### structural_pages.module (linhas 53-66) **Antes:** ```php if (in_array($parent_entity_type, ['user', 'group'])) { $entity->set('field_site_section', NULL); return; } ``` **Depois:** ```php $handler_manager = \Drupal::service('plugin.manager.parent_entity_handler'); if ($handler_manager->clearsSiteSection($parent_entity_type)) { $entity->set('field_site_section', NULL); return; } ``` ### StructuralPagesMenuBlock.php - Remover `use Drupal\group\Entity\GroupInterface;` - Injetar `ParentEntityHandlerManagerInterface` - Substituir `getAncestorFromRoute()`: ```php protected function getAncestorFromRoute(): ?EntityInterface { return $this->handlerManager->getEntityFromRoute($this->routeMatch); } ``` ### SectionBreadcrumbBuilder.php - Injetar `ParentEntityHandlerManagerInterface` - Substituir switch statement: ```php if ($context) { $this->handlerManager->buildBreadcrumbForEntity($breadcrumb, $context['entity']); } ``` - Remover métodos `addUserBreadcrumb()` e `addGroupBreadcrumb()` ### StructuralPagesSettingsForm.php - Injetar `ParentEntityHandlerManagerInterface` - Substituir `$supportedEntityTypes`: ```php $supportedEntityTypes = $this->handlerManager->getSupportedEntityTypes(); ``` - Substituir match statement de sort_field: ```php $sort_field = $this->handlerManager->getSortField($entity_type); ``` ### structural_pages.services.yml ```yaml services: plugin.manager.parent_entity_handler: class: Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerManager arguments: - '@container.namespaces' - '@cache.discovery' - '@module_handler' structural_pages.breadcrumb.section: class: Drupal\structural_pages\Breadcrumb\SectionBreadcrumbBuilder arguments: - '@entity_type.manager' - '@plugin.manager.parent_entity_handler' tags: - { name: breadcrumb_builder, priority: 100 } ``` --- ## Verificação 1. **Sem módulo group instalado:** - O módulo structural_pages deve funcionar normalmente - Formulário de configurações não deve mostrar opção "Groups" - Nenhum erro de classe não encontrada 2. **Com submódulo structural_pages_group ativado:** - Opção "Groups" aparece no formulário de configurações - Groups funcionam como entidades pai - Breadcrumbs mostram nome do grupo 3. **Extensibilidade:** - Criar handler de teste em módulo custom - Verificar que é descoberto automaticamente pelo manager 4. **Cache:** - Limpar cache após mudanças (`drush cr`) - Verificar que handlers são cacheados corretamente --- ## Compatibilidade - Configuração existente (`allowed_parent_targets`) continua funcionando - Nenhuma alteração no schema de configuração necessária - Backwards compatible - apenas mudança interna de implementação