rahpt / ci4-module-nav
Navigation and breadcrumb management system for CodeIgniter 4 modules
Requires
- php: ^8.1
- codeigniter4/framework: ^4.5
- rahpt/ci4-module: *
Requires (Dev)
- codeigniter4/devkit: ^1.2
- phpunit/phpunit: ^10.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Sistema corporativo de navegação, menus dinâmicos e breadcrumbs para módulos CodeIgniter 4. Possui filtragem recursiva em árvore com poda de nós vazios, chave canônica de submenus (children), resolução canônica de rotas nomeadas (route), renderizador seguro de ícones (IconRegistry), cache multi-tenant com invalidação reativa e rigorosa regra de autorização.
⚠️ Regra Central de Arquitetura
MENU NÃO É AUTORIZAÇÃO
A visibilidade de um item de menu destina-se exclusivamente à experiência de navegação do usuário (UI). Ocultar um link no menu não restringe o acesso ao recurso. A segurança e autorização reais devem ser obrigatoriamente aplicadas através de Filters, Shield (RBAC), Policies de Domínio e validações em Controllers/Services.
📋 Índice
- Características
- Instalação
- Definição Canônica de Menus no Módulo
- Árvore Canônica e Filtragem Recursiva
- Filtros de Visibilidade e Segurança
- IconRegistry Seguro
- Cache Multi-Tenant e ACL Versionado
- Breadcrumbs e Helpers
- Exemplo de Renderização em Layouts
- API Reference
- Histórico de Versões
- Licença
✨ Características
Navegação & Estrutura Canônica
- ✅ Chave Canônica
children- Submenus estruturados sob a chave padronizadachildrencom suporte retrocompatível automático para a chave legadaitems. - ✅ Filtragem Recursiva em Cascata - Avaliação profunda de permissões e tenancy em múltiplos níveis de aninhamento.
- ✅ Poda Automática de Menus Pais Vazios - Nós agrupadores sem URL própria cujos filhos forem todos ocultados por filtros são podados automaticamente da árvore de navegação.
- ✅ Rotas Nomeadas Canônicas (
route) - Resolução automática e segura viaurl_to($route)evitando acoplamento com URLs hardcoded. - ✅ Agrupamento por Categorias - Método
MenuRegistry::grouped()para categorizar menus em seções de navegação.
Segurança & Zero-Trust
- ✅ IconRegistry Seguro - Allowlist estrita de ícones evitando ataques de injeção de HTML e quebra de tags
<i>. - ✅ Sanitização contra XSS e Protocol Injection - Bloqueio determinístico de esquemas perigosos (
javascript:,data:,vbscript:) e escape HTML automático (label_escaped). - ✅ Filtro Shield (RBAC) - Visibilidade condicionada a permissões (
permission) e grupos (group). - ✅ Filtro Multi-Tenancy - Restrição por tenant ativo (
tenant => true), escopo global (tenant => false) ou lista de empresas permitidas.
Performance & Cache
- ✅ Cache Isolado Multi-Tenant/ACL - Chaves de cache segmentadas por tenant, usuário e hash curto das permissões ativas (
aclHash). - ✅ Invalidação Reativa sem Locks - Limpeza atômica em resposta ao evento
rahpt.module.changedincrementando a versão do cache.
🚀 Instalação
composer require rahpt/ci4-module-nav
📖 Definição Canônica de Menus no Módulo
Em qualquer módulo CodeIgniter 4 estendendo BaseModule, implemente o método menu() utilizando a convenção canônica:
<?php namespace App\Modules\Contratos\Config; use Rahpt\Ci4Module\BaseModule; class Module extends BaseModule { public string $name = 'Contratos'; public int $priority = 20; public function menu(): array { return [ [ 'id' => 'contracts.index', // Identificador único 'label' => 'Contratos', 'route' => 'contracts.index', // Rota nomeada canônica via url_to() 'icon' => 'contracts', // Resolvido com segurança via IconRegistry 'permission' => 'contracts.view', // Verificação Shield: $user->can('contracts.view') 'group' => ['admin', 'gestor'], // Verificação Shield: $user->inGroup(...) 'tenant' => true, // Apenas visível em contexto de tenant ativo 'order' => 10, 'children' => [ // Chave canônica para submenus [ 'id' => 'contracts.create', 'label' => 'Novo Contrato', 'route' => 'contracts.create', 'icon' => 'file', 'permission' => 'contracts.create' ], [ 'id' => 'contracts.reports', 'label' => 'Relatórios Financeiros', 'route' => 'contracts.reports', 'icon' => 'chart', 'permission' => 'contracts.reports' ] ] ] ]; } }
🌳 Árvore Canônica e Filtragem Recursiva
O processador MenuRegistry::applyFilters() executa as seguintes etapas:
- Validação de Visibilidade: Verifica se o nó atende aos requisitos de autenticação, permissões e tenant.
- Normalização: Converte qualquer chave legada
itemspara o padrãochildren. - Recursão Profunda: Processa todos os níveis de submenus da árvore.
- Poda de Nós Órfãos: Se um item pai serve apenas como agrupador (sem
routeouurl) e todos os seus filhos forem ocultados pelas regras de permissão, o pai é removido automaticamente, evitando menus colapsáveis vazios na UI.
🛡️ Filtros de Visibilidade e Segurança
1. Permissões e Grupos do CodeIgniter Shield
permission: Exige que o usuário possua a permissão especificada ($user->can($permission)).group: Exige que o usuário pertença ao grupo indicado ($user->inGroup(...)).
2. Multi-Tenancy
tenant => true: O item é exibido apenas quando houver um tenant ativo no contexto (has_tenant() === true).tenant => false: O item é exibido apenas no painel administrativo global da plataforma (sem tenant).tenant => ['empresa-a', 'empresa-b']: O item é restrito à lista especificada de empresas.
3. Sanitização contra Protocol Injection
- Esquemas de URL inseguros (
javascript:,data:,vbscript:) são bloqueados e convertidos para#. - O label seguro e escapado contra ataques XSS fica disponível na chave
label_escaped.
🎨 IconRegistry Seguro
O IconRegistry neutraliza ataques de injeção em tags <i> através de allowlist estrita de ícones homologados:
use Rahpt\Ci4ModuleNav\Support\IconRegistry; // 1. Resolução segura da classe CSS correspondente $class = IconRegistry::resolve('contracts'); // Retorna: 'fas fa-file-contract' // 2. Renderização direta da tag HTML segura echo IconRegistry::render('dashboard', 'mr-2 text-primary'); // Retorna: '<i class="fas fa-tachometer-alt mr-2 text-primary"></i>' // 3. Registro de novos ícones homologados IconRegistry::register('pix', 'fab fa-pix');
⚡ Cache Multi-Tenant e ACL Versionado
O MenuRegistry mantém cache segmentado com isolamento total entre tenants e níveis de acesso:
module_menus_v{version}_{tenantId}_{userId}_acl{aclHash}
version: Versão global do cache, incrementada atomicamente quando qualquer módulo é ativado ou desativado.tenantId: Impede que a visualização de uma organização vaze para outra.userId: Garante o isolamento individual do usuário.aclHash: Hash determinístico baseado nos grupos e permissões ativas do usuário.
🍞 Breadcrumbs e Helpers
// No Controller set_breadcrumb('Início', '/'); set_breadcrumb('Contratos', 'contratos'); set_breadcrumb('Editar Contrato #42'); // Sem segundo parâmetro: marca página ativa // Na View <?= render_breadcrumbs(' / ') ?>
🖥️ Exemplo de Renderização em Layouts (AdminLTE)
<?php use Rahpt\Ci4ModuleNav\MenuRegistry; use Rahpt\Ci4ModuleNav\Support\IconRegistry; $menus = MenuRegistry::all(); ?> <ul class="nav nav-pills nav-sidebar flex-column" data-widget="treeview" role="menu"> <?php foreach ($menus as $item): ?> <?php $hasChildren = !empty($item['children']); ?> <li class="nav-item <?= $hasChildren ? 'has-treeview' : '' ?>"> <a href="<?= base_url($item['url'] ?? '#') ?>" class="nav-link"> <?= IconRegistry::render($item['icon'] ?? 'circle', 'nav-icon') ?> <p> <?= $item['label_escaped'] ?? $item['label'] ?> <?php if ($hasChildren): ?> <i class="right fas fa-angle-left"></i> <?php endif; ?> </p> </a> <?php if ($hasChildren): ?> <ul class="nav nav-treeview pl-3"> <?php foreach ($item['children'] as $child): ?> <li class="nav-item"> <a href="<?= base_url($child['url']) ?>" class="nav-link"> <?= IconRegistry::render($child['icon'] ?? 'circle', 'nav-icon') ?> <p><?= $child['label_escaped'] ?? $child['label'] ?></p> </a> </li> <?php endforeach; ?> </ul> <?php endif; ?> </li> <?php endforeach; ?> </ul>
🔧 API Reference
MenuRegistry::all(): array: Retorna a árvore hierárquica consolidada de menus ativos com filtros e sanitização aplicados.MenuRegistry::grouped(): array: Retorna os menus organizados por categorias e seções.MenuRegistry::clearCache(): void: Incrementa a versão do cache, invalidando instantaneamente os menus cacheados.
🕒 Histórico de Versões
[1.4.0] - 2026-09-26
- Novo: Adoção canônica da chave
childrenpara submenus, mantendo suporte retrocompatível transparente aitems. - Novo: Filtragem recursiva em múltiplos níveis na árvore hierárquica de menus.
- Novo: Poda automática de nós agrupadores pais sem URL quando todos os filhos forem ocultados por permissões.
- Melhoria: Formalização da regra arquitetural
MENU != AUTHORIZATION. - Melhoria: Resolução de rotas canônicas via
url_to()a partir da chaveroute.
[1.3.0] - 2026-09-26
- Novo:
IconRegistrycentralizado com allowlist e renderização segura contra injeção de código. - Novo: Filtros nativos de grupos e permissões do CodeIgniter Shield.
- Novo: Isolamento por Multi-Tenancy e perfis de módulos.
- Novo: Cache atômico com versionamento e hash ACL.
[1.0.1] - 2026-02-15
- Versão inicial estável da biblioteca de navegação.
📄 Licença
Distribuído sob a licença MIT. Veja LICENSE para mais detalhes.
Desenvolvido por Rahpt
Mantido pela equipe Rahpt / CodeIgniter 4 Modular Platform.