mirror of
https://gitlab.unicamp.br/infimecc_drupal11_modules/structural_pages.git
synced 2026-03-09 10:07:40 -03:00
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>
238 lines
7.5 KiB
Markdown
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
|