First Commit

This commit is contained in:
marco
2026-09-01 10:46:29 -03:00
commit bf96b77e9c
10000 changed files with 3508600 additions and 0 deletions
+457
View File
@@ -0,0 +1,457 @@
# 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):
```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
<?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):**
> ```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
<?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
<?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
<?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
<?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:
```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/<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):**
```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`