# Manual: Como criar módulos para instalar no SysCMS Este manual orienta a criação, estrutura e instalação de módulos compatíveis com o SysCMS, seguindo as diretrizes do projeto. ## Estrutura de Diretórios do Módulo Crie o módulo dentro de `modules//` em minúsculo. ``` modules/ / classes/ Controllers/ Models/ Services/ routes/ web.php api.php views/ index.twig admin/ locales/ pt_BR/ en_US/ module.json ``` ## Arquivo `module.json` Define metadados, rotas, resources e providers. Exemplo mínimo (Company): ```json { "name": "company", "display_name": "Company", "description": "Company management module", "version": "1.0.0", "author": "SysCMS Team", "dependencies": ["auth"], "permissions": [ "company.view", "company.create", "company.edit", "company.delete" ], "settings": {}, "providers": [ "Modules\\Company\\Providers\\CompanyServiceProvider" ], "routes": { "web": "routes/web.php" }, "menu": { "dashboard": { "icon": "bi bi-building", "label": "Companies", "route": "admin/companies", "order": 30 } }, "views": "views", "locales": "locales", "module_ative": true, "module_public": false, "module_blocked": false } ``` ## Registro de Rotas Declare as rotas em `routes/web.php` e/ou `routes/api.php` retornando uma função que receba a aplicação e chame `addRoute`. ```php addRoute('GET', '/meu-modulo', [\Modules\MeuModulo\Controllers\HomeController::class, 'index']); $app->addRoute('POST', '/meu-modulo/salvar', [\Modules\MeuModulo\Controllers\HomeController::class, 'store']); }; ``` - Carregamento automático de rotas de módulos: `kernel/ModuleLoader.php:142–170` ## Empacotamento para Instalação (.zip) Para que o módulo seja instalável via painel administrativo (`/admin/modules/install`), ele deve ser compactado em um arquivo `.zip`. > **⚠️ IMPORTANTE: Geração do ZIP** > Ao criar o arquivo `.zip`, certifique-se de que a estrutura interna não contenha caminhos absolutos ou barras invertidas (`\`) típicas do Windows. > > **Estrutura Correta do ZIP:** > Ao abrir o zip, os arquivos `module.json`, `routes/`, `classes/`, etc., devem estar **na raiz** ou dentro de uma pasta única com o nome do módulo (o instalador trata ambos os casos, mas prefira a raiz para simplicidade). > > **Comando Recomendado (Linux/Mac/WSL):** > ```bash > cd modules/meumodulo > zip -r ../meumodulo.zip . > ``` > > **Comando Recomendado (Windows - PowerShell):** > Evite usar `Compress-Archive` diretamente se ele gerar barras invertidas. Prefira usar `tar` ou ferramentas como 7-Zip garantindo o formato ZIP. > ```powershell > cd modules/meumodulo > tar -a -c -f ../../meumodulo.zip * > ``` ## Banco de Dados e Migrações Todo módulo deve conter suas próprias migrações na pasta `database/migrations/`. > **⚠️ REGRA CRÍTICA 1: Imutabilidade do Core** > Um novo módulo **NUNCA** deve modificar a estrutura (schema) de tabelas existentes do sistema (Core ou outros módulos). Ele deve criar apenas suas próprias tabelas. Se precisar de relacionamento, use chaves estrangeiras sem alterar a tabela referenciada. > > **⚠️ REGRA CRÍTICA 2: Independência na Instalação** > As migrações (especialmente a de inserção do módulo) **NUNCA** devem depender de Classes/Models de outros módulos (ex: `Modules\Company\Models\Company`), pois eles podem não estar carregados no momento da instalação. > > **Forma Correta:** Use `Capsule::table('nome_tabela')->insert(...)` para inserir dados em tabelas de outros módulos. > > **Exemplo Incorreto (NÃO FAÇA):** > ```php > $company = \Modules\Company\Models\Company::find(1); // Erro se o módulo Company não estiver carregado > ``` > > **Exemplo Correto:** > ```php > Capsule::table('bus')->where('bus_id', 1)->update([...]); > ``` ### 1. Criação de Tabelas Crie arquivos de migração para definir a estrutura do banco de dados do módulo. Exemplo: `001_create_meumodulo_tables.php`. ```php use Illuminate\Database\Capsule\Manager as Capsule; use Illuminate\Database\Schema\Blueprint; class CreateMeuModuloTables { public function up() { if (!Capsule::schema()->hasTable('meumodulo_items')) { Capsule::schema()->create('meumodulo_items', function (Blueprint $table) { $table->increments('id'); $table->string('title'); $table->timestamps(); }); } } public function down() { Capsule::schema()->dropIfExists('meumodulo_items'); } } ``` ### 2. Registro do Módulo (Instalação) É obrigatório criar uma migração para registrar o módulo no sistema (tabelas `modules` e `bus_modules`). Isso garante que o módulo seja reconhecido e ativado após a instalação. Exemplo: `002_insert_meumodulo_record.php`. ```php use Illuminate\Database\Capsule\Manager as Capsule; use App\Models\Module; use Modules\Company\Models\Company; class InsertMeuModuloRecord { public function up() { $moduleData = [ 'modules_name' => 'meumodulo', 'modules_display_name' => 'Meu Módulo', 'modules_version' => '1.0.0', // ... outros campos do module.json ... ]; $existingModule = Module::where('modules_name', 'meumodulo')->first(); if (!$existingModule) { $module = Module::create($moduleData); // Associar a todas as empresas existentes if (Capsule::schema()->hasTable('bus')) { $companies = Company::all(); foreach ($companies as $company) { // Lógica de inserção na tabela bus_modules } } } } public function down() { // Lógica para remover o módulo } } ``` ## Controllers Coloque controllers em `classes/Controllers/`. Exemplo: ```php render('meumodulo/views/index.twig', [ 'title' => 'Meu Módulo' ])); } } ``` ## Views e Locales - Views: declare pasta em `module.json` via chave `views` e renderize com o namespace `/views/...`. - Locales: declare pasta em `module.json` via chave `locales`. O sistema registra e utiliza `Translator`. - Registro automático: `kernel/ModuleLoader.php:175–192` ## Service Providers Use providers para inicializar integrações, hooks, listeners e configurações dinâmicas do core (como rotas públicas). Registre-os em `module.json` na chave `providers`. ### Exemplo Completo de ServiceProvider ```php addPublicPath('/meu-modulo/landing-page'); $app->addPublicPath('/meu-modulo/public-api/*'); // 2. Registrar Middlewares // $app->addGlobalMiddleware(new \Modules\MeuModulo\Middleware\GlobalGuard()); // 3. Registrar Hooks add_action('meumodulo.evento', function ($payload) { // lógica }); add_filter('meumodulo.sanitize', function ($value) { return trim(strip_tags((string)$value)); }); // 4. Injetar variáveis globais no Twig try { $view = new View(); $view->getEngine()->addGlobal('meu_modulo_config', ['active' => true]); } catch (\Throwable $e) { // Ignorar erros se view não estiver pronta } } } ``` - API de Hooks: `COMO_USAR_HOOK.md` - HookManager disponível em: `kernel/HookManager.php` - Exposição via aplicação: `kernel/Application.php:50–58` (getHookManager) - **IMPORTANTE**: Use o Service Provider para evitar alterar arquivos do core (`Application.php`). ## Migrations Para compatibilidade com o comando de migração, coloque migrations em `app/database/migrations/`. - Exemplo de criação de tabela: ```php hasTable('meumodulo')) { Capsule::schema()->create('meumodulo', function (Blueprint $table) { $table->id(); $table->string('title'); $table->timestamps(); }); } } public function down(): void { Capsule::schema()->dropIfExists('meumodulo'); } } ``` - Para registrar o módulo na tabela `modules`, crie uma migration que insere o registro (recomendado na pasta principal `app/database/migrations`). ### Exemplo: Registrar o módulo Company Crie um arquivo como `0xx_register_company_module.php` em `app/database/migrations/` com o conteúdo abaixo: ```php exists()) { Module::create([ 'module_name' => 'company', 'module_display_name' => 'Company', 'module_description' => 'Company management module', 'module_version' => '1.0.0', 'module_author' => 'SysCMS Team', 'module_ative' => true, 'module_public' => false, 'module_blocked' => false, 'module_dependencies' => ['auth'], 'module_permissions' => [ 'company.view', 'company.create', 'company.edit', 'company.delete' ], 'module_settings' => [ 'menu' => [ 'dashboard' => [ 'icon' => 'bi bi-building', 'label' => 'Companies', 'route' => 'admin/companies', 'order' => 30 ] ] ], 'module_path' => 'modules/company', 'module_namespace' => 'Modules\\Company', 'module_icon' => 'bi bi-building', 'module_order' => 30, 'module_install_date' => Carbon::now(), 'module_update_date' => Carbon::now() ]); } } public function down(): void { $module = Module::where('module_name', 'company')->first(); if ($module) { $module->delete(); } } } ``` Depois, execute: ```bash php syscli migrate ``` ## Permissões e Menu - Defina permissões no `module.json` (`permissions`). - Defina itens de menu no `module.json` (`menu`), usando ícones somente do Bootstrap Icons. - Regra: `RULES.md:40–74` ## Instalação do Módulo 1. Copie o diretório do módulo para `modules//`. 2. Garanta que `module.json` está correto. 3. Adicione migrations necessárias em `app/database/migrations/`. 4. Execute migrações: - `php syscli migrate` 5. Verifique rotas carregadas e acessíveis. 6. Se o módulo requer habilitação por empresa, utilize o painel ou scripts próprios para vincular em `bus_modules` (conforme política da aplicação). - Carregamento de módulos: `kernel/ModuleLoader.php:184–192` (loadAll) - Roteamento da aplicação: `kernel/Application.php:199–217` ## Empacotamento do Módulo (.zip) Ao criar um novo módulo, é **obrigatório** criar também o arquivo `.zip` correspondente para permitir a instalação via painel administrativo. **Regras para o ZIP:** 1. O arquivo `module.json` deve estar na raiz do arquivo compactado (não dentro de uma pasta com o nome do módulo). 2. O nome do arquivo zip deve corresponder ao nome do módulo (ex: `modelo.zip`). **Comando sugerido (PowerShell):** ```powershell Compress-Archive -Path modules/nome_do_modulo/* -DestinationPath nome_do_modulo.zip -Force ``` ## Instalador via Dashboard (Super Admin) - Acesso: `GET /admin/modules/install` mapeado em `routes/web.php:41–45` e renderiza `templates/modules/install.twig` (`templates/modules/install.twig:1–5`). - Formulário: `POST /admin/modules/install` com `multipart/form-data`; campos `module_zip` (arquivo `.zip`) e opcional `enable_for_all` (`templates/modules/install.twig:44–59`). - Validações: tipo de arquivo ZIP e presença de `module.json` (`app/app/Controllers/AdminController.php:215–224`, `app/app/Controllers/AdminController.php:291–300`). Campos obrigatórios em `module.json`: `name`, `version`, `description` (`app/app/Controllers/AdminController.php:323–332`). - Instalação: extração para diretório temporário, cópia para `modules/`, execução de migrations em `database/migrations` se existirem (`app/app/Controllers/AdminController.php:230–239`, `app/app/Controllers/AdminController.php:252–258`, `app/app/Controllers/AdminController.php:393–416`). - Registro: criação do registro em `modules` com metadados do `module.json` (`app/app/Controllers/AdminController.php:461–484`). - Habilitação: se marcado `enable_for_all`, habilita o módulo para todas as empresas (`app/app/Controllers/AdminController.php:492–498`). - Pós-instalação: limpeza de temporários e exibição de mensagens de sucesso/erro via flash (`app/app/Controllers/AdminController.php:267–283`). - Correção de menus: `GET /admin/modules/fix-menus` atualiza `module_settings.menu` consultando `module.json` (`routes/web.php:41–45`, `app/app/Controllers/AdminController.php:423–453`). ### Estrutura exigida no ZIP - `module.json` com configurações do módulo. - `classes/` com controladores, modelos e serviços. - `views/` com templates Twig. - `database/migrations/` com migrações (opcional). ## Hooks Recomendados - `meumodulo.installed`: após instalação concluída (migration + registro). - `meumodulo.uninstalled`: antes de remover dados. - `meumodulo.updated`: após atualização de versão. ## Boas Práticas - Seguir `RULES.md` e usar somente Bootstrap Icons. - Não expor segredos; use `.env`. - Respeitar estrutura e namespaces: `Modules\\\\...` e pasta `modules//` em minúsculo. - Validar entrada de dados, usar filters para sanitização. - Manter compatibilidade com os middlewares da aplicação; - Rotas públicas devem estar na lista pública se necessário (ver `kernel/Application.php:279–311`). ## Verificação - Após instalar, acesse rotas do módulo e verifique renderização. - Se aplicável, execute testes (`composer test`). - Cheque logs em `logs/app.log` para erros em providers/hooks. ## Exemplo Real (Company) - Rotas admin do módulo: `modules/company/routes/web.php:6–13` - Controller lista e gerencia empresas: `modules/company/classes/Controllers/CompanyController.php:14–22` (index) e `35–82` (store) - Views admin: `modules/company/views/admin/index.twig:13–23` (header) e tabela `25–71` - `module.json` configurado com rotas, views e menu: `modules/company/module.json:1–30`