Files
structural_pages/docs/PLUGIN_SYSTEM_PLAN.md
Quintino A. G. Souza 88b9605408 Rename module site_structure → structural_pages and vocabulary site_section → site_sections
Renames the module from site_structure to structural_pages and pluralizes
the taxonomy vocabulary machine name from site_section to site_sections,
updating all config, PHP, translations, and documentation references
while preserving field_site_section, clears_site_section, and $site_section_id.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 08:10:32 -03:00

238 lines
7.5 KiB
Markdown

# 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