Files
dockerfluxodecaixapro/rules/12_MANUAL_COMO_CRIAR_MODULOS_PARA_INSTALAR.md
2026-09-01 10:46:29 -03:00

16 KiB
Executable File
Raw Permalink Blame History

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/<nome>/ em minúsculo.

modules/
  <nome>/
    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):

{
  "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
use Kernel\Application;

return function (Application $app) {
    $app->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):

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.

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):

$company = \Modules\Company\Models\Company::find(1); // Erro se o módulo Company não estiver carregado

Exemplo Correto:

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.

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.

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
namespace Modules\MeuModulo\Controllers;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class HomeController
{
    public function index(Request $request): Response
    {
        return new Response((new \Kernel\View())->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 <modulo>/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
namespace Modules\MeuModulo\Providers;

use Kernel\Application;
use Kernel\View;

class MeuModuloServiceProvider
{
    public function register(): void {}

    public function boot(): void
    {
        $app = Application::getInstance();

        // 1. Registrar rotas públicas (que não exigem login)
        $app->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
use Illuminate\Database\Capsule\Manager as Capsule;
use Illuminate\Database\Schema\Blueprint;

class CreateMeuModuloTable
{
    public function up(): void
    {
        if (!Capsule::schema()->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

use App\Models\Module;
use Carbon\Carbon;

class RegisterCompanyModule
{
    public function up(): void
    {
        if (!Module::where('module_name', 'company')->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:

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/<nome>/.
  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):

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/<name>, 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\\<Nome>\\... e pasta modules/<nome>/ 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