16 KiB
Executable File
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-Archivediretamente se ele gerar barras invertidas. Prefira usartarou 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 carregadoExemplo 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.jsonvia chaveviewse renderize com o namespace<modulo>/views/.... -
Locales: declare pasta em
module.jsonvia chavelocales. O sistema registra e utilizaTranslator. -
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 principalapp/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
- Regra:
Instalação do Módulo
- Copie o diretório do módulo para
modules/<nome>/. - Garanta que
module.jsonestá correto. - Adicione migrations necessárias em
app/database/migrations/. - Execute migrações:
php syscli migrate
- Verifique rotas carregadas e acessíveis.
- 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:
- O arquivo
module.jsondeve estar na raiz do arquivo compactado (não dentro de uma pasta com o nome do módulo). - 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/installmapeado emroutes/web.php:41–45e renderizatemplates/modules/install.twig(templates/modules/install.twig:1–5). - Formulário:
POST /admin/modules/installcommultipart/form-data; camposmodule_zip(arquivo.zip) e opcionalenable_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 emmodule.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 emdatabase/migrationsse 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
modulescom metadados domodule.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-menusatualizamodule_settings.menuconsultandomodule.json(routes/web.php:41–45,app/app/Controllers/AdminController.php:423–453).
Estrutura exigida no ZIP
module.jsoncom 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.mde usar somente Bootstrap Icons. - Não expor segredos; use
.env. - Respeitar estrutura e namespaces:
Modules\\<Nome>\\...e pastamodules/<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).
- Rotas públicas devem estar na lista pública se necessário (ver
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.logpara 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) e35–82(store) - Views admin:
modules/company/views/admin/index.twig:13–23(header) e tabela25–71 module.jsonconfigurado com rotas, views e menu:modules/company/module.json:1–30