netgescon-day0/docs/06-sviluppo-moduli/SPECIFICA-SVILUPPO-MODULI.md

814 lines
23 KiB
Markdown

# 📋 GUIDA COMPLETA SVILUPPO MODULI NETGESCON
**Versione: 1.0 - Data: 31 Luglio 2025**
---
## 🎯 **INTRODUZIONE**
NetGescon utilizza un sistema modulare avanzato che permette di sviluppare, installare e monetizzare moduli aggiuntivi. Ogni modulo è completamente autonomo e può essere distribuito come prodotto commerciale.
### **🏗️ ARCHITETTURA MODULARE**
```
NetGescon/
├── app/
│ ├── Services/
│ │ ├── ModuleManager.php # Gestione moduli
│ │ └── HookManager.php # Sistema eventi
│ └── Modules/
│ └── [NomeModulo]/
│ ├── module.json # Configurazione modulo
│ ├── Controllers/ # Controller Laravel
│ ├── Routes/ # File route
│ ├── Migrations/ # Migrazioni database
│ ├── Seeders/ # Dati iniziali
│ ├── Views/ # Template Blade
│ └── Assets/ # CSS/JS specifici
```
---
## 🔧 **STRUTTURA MODULO STANDARD**
### **1. Configurazione Base (module.json)**
```json
{
"name": "NomeModulo",
"display_name": "Nome Visualizzato",
"version": "1.0.0",
"description": "Descrizione del modulo",
"author": "Nome Sviluppatore",
"email": "email@sviluppatore.com",
"license": "commercial",
"price": 99.00,
"currency": "EUR",
"billing": "yearly",
"requirements": {
"laravel": ">=10.0",
"php": ">=8.1",
"netgescon": ">=1.0"
},
"dependencies": [],
"permissions": {
"modulo_view": "Visualizzare il modulo",
"modulo_create": "Creare elementi",
"modulo_edit": "Modificare elementi",
"modulo_delete": "Eliminare elementi",
"modulo_admin": "Amministrazione completa"
},
"menu_items": [
{
"title": "Dashboard Modulo",
"route": "modulo.index",
"icon": "fa-dashboard",
"permission": "modulo_view",
"order": 1
}
],
"hooks": [
"modulo.before_create",
"modulo.after_create",
"modulo.before_update",
"modulo.after_update",
"modulo.before_delete",
"modulo.after_delete"
],
"config": {
"auto_install_routes": true,
"auto_install_migrations": true,
"cache_enabled": true,
"api_enabled": true
}
}
```
### **2. Controller Base**
```php
<?php
namespace App\Modules\NomeModulo\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
use App\Services\HookManager;
class NomeModuloController extends Controller
{
protected $hookManager;
public function __construct()
{
// Middleware di autenticazione
$this->middleware('auth');
// Middleware permessi specifici del modulo
$this->middleware('permission:modulo_view')->only(['index', 'show']);
$this->middleware('permission:modulo_create')->only(['create', 'store']);
$this->middleware('permission:modulo_edit')->only(['edit', 'update']);
$this->middleware('permission:modulo_delete')->only(['destroy']);
$this->hookManager = app(HookManager::class);
}
public function index(Request $request)
{
try {
// Hook pre-azione
$this->hookManager->execute('modulo.before_index', [$request]);
// Logica del controller
$data = DB::table('modulo_table')->paginate(20);
// Hook post-azione
$this->hookManager->execute('modulo.after_index', [$data]);
return view('modules.nome-modulo.index', compact('data'));
} catch (\Exception $e) {
Log::error("Errore modulo: " . $e->getMessage());
return back()->with('error', 'Errore: ' . $e->getMessage());
}
}
// Altri metodi CRUD...
}
```
### **3. Routes (web.php)**
```php
<?php
use Illuminate\Support\Facades\Route;
use App\Modules\NomeModulo\Controllers\NomeModuloController;
// Gruppo route con middleware
Route::middleware(['auth', 'verified'])->group(function () {
// Route principali
Route::prefix('nome-modulo')->name('modulo.')->group(function () {
Route::get('/', [NomeModuloController::class, 'index'])->name('index');
Route::get('/create', [NomeModuloController::class, 'create'])->name('create');
Route::post('/', [NomeModuloController::class, 'store'])->name('store');
Route::get('/{id}', [NomeModuloController::class, 'show'])->name('show');
Route::get('/{id}/edit', [NomeModuloController::class, 'edit'])->name('edit');
Route::put('/{id}', [NomeModuloController::class, 'update'])->name('update');
Route::delete('/{id}', [NomeModuloController::class, 'destroy'])->name('destroy');
});
// Route API per AJAX
Route::prefix('api/nome-modulo')->name('api.modulo.')->group(function () {
Route::get('/data', [NomeModuloController::class, 'getData'])->name('data');
Route::post('/action', [NomeModuloController::class, 'action'])->name('action');
});
});
```
### **4. Migration Standard**
```php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
// Tabella principale del modulo
Schema::create('modulo_table', function (Blueprint $table) {
$table->id();
$table->string('nome');
$table->text('descrizione')->nullable();
$table->enum('status', ['attivo', 'inattivo'])->default('attivo');
$table->json('config')->nullable();
// Audit fields standard
$table->unsignedBigInteger('created_by')->nullable();
$table->unsignedBigInteger('updated_by')->nullable();
$table->timestamps();
$table->softDeletes();
// Indici per performance
$table->index(['status', 'created_at']);
$table->index('nome');
// Foreign keys
$table->foreign('created_by')->references('id')->on('users')->onDelete('set null');
$table->foreign('updated_by')->references('id')->on('users')->onDelete('set null');
});
// Tabella log/cronologia
Schema::create('modulo_history', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('record_id');
$table->string('action');
$table->json('old_data')->nullable();
$table->json('new_data')->nullable();
$table->unsignedBigInteger('user_id')->nullable();
$table->timestamp('created_at');
$table->index(['record_id', 'created_at']);
$table->foreign('record_id')->references('id')->on('modulo_table')->onDelete('cascade');
$table->foreign('user_id')->references('id')->on('users')->onDelete('set null');
});
}
public function down(): void
{
Schema::dropIfExists('modulo_history');
Schema::dropIfExists('modulo_table');
}
};
```
### **5. Seeder con Dati Base**
```php
<?php
namespace App\Modules\NomeModulo\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class NomeModuloSeeder extends Seeder
{
public function run(): void
{
// Configurazioni base
$configs = [
[
'chiave' => 'modulo.default_setting',
'valore' => 'valore_default',
'descrizione' => 'Impostazione di default',
'created_at' => now(),
'updated_at' => now()
]
];
foreach ($configs as $config) {
DB::table('modulo_config')->insert($config);
}
// Dati di esempio (solo in development)
if (app()->environment('local', 'development')) {
$esempi = [
[
'nome' => 'Esempio 1',
'descrizione' => 'Dato di esempio',
'status' => 'attivo',
'created_by' => 1,
'created_at' => now(),
'updated_at' => now()
]
];
foreach ($esempi as $esempio) {
DB::table('modulo_table')->insert($esempio);
}
}
echo "✅ Seeder NomeModulo completato\n";
}
}
```
---
## 🔌 **SISTEMA HOOK E EVENTI**
### **Hook Disponibili nel Sistema**
```php
// Hook di sistema core
'app.boot' // All'avvio dell'applicazione
'app.before_request' // Prima di ogni richiesta
'app.after_request' // Dopo ogni richiesta
// Hook moduli
'module.before_install' // Prima installazione modulo
'module.after_install' // Dopo installazione modulo
'module.before_uninstall' // Prima disinstallazione
'module.after_uninstall' // Dopo disinstallazione
// Hook utenti
'user.before_login' // Prima del login
'user.after_login' // Dopo il login
'user.before_logout' // Prima del logout
// Hook del tuo modulo
'modulo.before_create' // Prima della creazione
'modulo.after_create' // Dopo la creazione
'modulo.before_update' // Prima dell'aggiornamento
'modulo.after_update' // Dopo l'aggiornamento
'modulo.before_delete' // Prima dell'eliminazione
'modulo.after_delete' // Dopo l'eliminazione
```
### **Utilizzo Hook nel Modulo**
```php
// Nel controller
$this->hookManager->execute('modulo.before_create', [$data]);
// Registrazione hook listener (nel boot del modulo)
$this->hookManager->register('modulo.after_create', function($data) {
// Logica eseguita dopo la creazione
Log::info('Nuovo elemento creato: ' . $data['nome']);
});
```
---
## 💾 **DATABASE E PERFORMANCE**
### **Best Practices Database**
1. **Nomi Tabelle**: Sempre prefisso modulo (`modulo_tabella`)
2. **Indici**: Su campi più interrogati (`status`, `created_at`, ecc.)
3. **Foreign Keys**: Sempre con `onDelete('set null')` o `cascade`
4. **Soft Deletes**: Per dati importanti
5. **JSON Fields**: Per configurazioni flessibili
6. **Audit Trail**: Campi `created_by`, `updated_by`, `deleted_by`
### **Ottimizzazioni Consigliate**
```php
// Uso di scope nei Model
public function scopeAttivo($query)
{
return $query->where('status', 'attivo');
}
// Cache per query pesanti
$data = Cache::tags(['modulo_data'])->remember('modulo_list', 3600, function() {
return DB::table('modulo_table')->where('status', 'attivo')->get();
});
// Query Builder ottimizzate
$risultati = DB::table('modulo_table')
->select(['id', 'nome', 'status']) // Solo campi necessari
->where('status', 'attivo')
->orderBy('created_at', 'desc')
->limit(20)
->get();
```
---
## 🔐 **SICUREZZA E PERMESSI**
### **Sistema Permessi**
```php
// Definizione permessi nel module.json
"permissions": {
"modulo_view": "Visualizzare elementi del modulo",
"modulo_create": "Creare nuovi elementi",
"modulo_edit": "Modificare elementi esistenti",
"modulo_delete": "Eliminare elementi",
"modulo_export": "Esportare dati",
"modulo_admin": "Amministrazione completa modulo"
}
// Uso nei controller
$this->middleware('permission:modulo_view')->only(['index', 'show']);
$this->middleware('permission:modulo_create')->only(['create', 'store']);
// Controllo nei template Blade
@can('modulo_create')
<a href="{{ route('modulo.create') }}" class="btn btn-primary">Nuovo</a>
@endcan
```
### **Validazione Input**
```php
// Nel controller
public function store(Request $request)
{
$validated = $request->validate([
'nome' => 'required|string|max:255|unique:modulo_table',
'email' => 'required|email',
'importo' => 'required|numeric|min:0',
'data' => 'required|date|after:today'
]);
// Sanitizzazione aggiuntiva
$validated['nome'] = strip_tags($validated['nome']);
// Creazione record...
}
```
---
## 📦 **INSTALLAZIONE E DISTRIBUZIONE**
### **Processo di Installazione**
1. **Preparazione ZIP**:
```
NomeModulo.zip
├── module.json
├── Controllers/
├── Routes/
├── Migrations/
├── Seeders/
└── Views/
```
2. **Installazione via ModuleManager**:
```php
$moduleManager = app(ModuleManager::class);
$result = $moduleManager->installModule('NomeModulo', $zipPath);
```
3. **Registrazione Automatica**:
- Route caricate automaticamente
- Migration eseguite
- Permessi registrati
- Menu integrato
### **Command Artisan per Installazione**
```php
// app/Console/Commands/InstallModule.php
class InstallModule extends Command
{
protected $signature = 'module:install {name} {--zip=}';
public function handle()
{
$name = $this->argument('name');
$zipPath = $this->option('zip');
$moduleManager = app(ModuleManager::class);
try {
$result = $moduleManager->installModule($name, $zipPath);
$this->info("✅ Modulo {$name} installato con successo!");
} catch (\Exception $e) {
$this->error("❌ Errore installazione: " . $e->getMessage());
}
}
}
```
---
## 🎨 **FRONTEND E UX**
### **Template Blade Base**
```blade
{{-- resources/views/modules/nome-modulo/index.blade.php --}}
@extends('layouts.app')
@section('title', 'Nome Modulo')
@section('content')
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h3 class="card-title">Nome Modulo</h3>
@can('modulo_create')
<a href="{{ route('modulo.create') }}" class="btn btn-primary">
<i class="fas fa-plus"></i> Nuovo
</a>
@endcan
</div>
<div class="card-body">
{{-- Filtri di ricerca --}}
<form method="GET" class="mb-3">
<div class="row">
<div class="col-md-4">
<input type="text" name="search" class="form-control"
placeholder="Cerca..." value="{{ request('search') }}">
</div>
<div class="col-md-2">
<button type="submit" class="btn btn-outline-primary">
<i class="fas fa-search"></i>
</button>
</div>
</div>
</form>
{{-- Tabella dati --}}
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Nome</th>
<th>Status</th>
<th>Data Creazione</th>
<th>Azioni</th>
</tr>
</thead>
<tbody>
@forelse($data as $item)
<tr>
<td>{{ $item->nome }}</td>
<td>
<span class="badge badge-{{ $item->status == 'attivo' ? 'success' : 'secondary' }}">
{{ ucfirst($item->status) }}
</span>
</td>
<td>{{ $item->created_at->format('d/m/Y H:i') }}</td>
<td>
@can('modulo_view')
<a href="{{ route('modulo.show', $item->id) }}" class="btn btn-sm btn-info">
<i class="fas fa-eye"></i>
</a>
@endcan
@can('modulo_edit')
<a href="{{ route('modulo.edit', $item->id) }}" class="btn btn-sm btn-warning">
<i class="fas fa-edit"></i>
</a>
@endcan
@can('modulo_delete')
<form method="POST" action="{{ route('modulo.destroy', $item->id) }}"
style="display: inline-block;">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-sm btn-danger"
onclick="return confirm('Sei sicuro?')">
<i class="fas fa-trash"></i>
</button>
</form>
@endcan
</td>
</tr>
@empty
<tr>
<td colspan="4" class="text-center">Nessun elemento trovato</td>
</tr>
@endforelse
</tbody>
</table>
</div>
{{-- Paginazione --}}
{{ $data->links() }}
</div>
</div>
</div>
</div>
</div>
@endsection
```
### **CSS/JS Specifici del Modulo**
```css
/* public/css/modules/nome-modulo.css */
.modulo-card {
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.modulo-stats {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 8px;
padding: 20px;
}
```
---
## 🧪 **TESTING E DEBUG**
### **Unit Test Base**
```php
<?php
namespace Tests\Modules\NomeModulo;
use Tests\TestCase;
use App\Modules\NomeModulo\Controllers\NomeModuloController;
class NomeModuloTest extends TestCase
{
public function test_index_displays_data()
{
$user = User::factory()->create();
$user->givePermissionTo('modulo_view');
$response = $this->actingAs($user)->get(route('modulo.index'));
$response->assertStatus(200);
$response->assertViewIs('modules.nome-modulo.index');
}
public function test_create_requires_permission()
{
$user = User::factory()->create();
$response = $this->actingAs($user)->get(route('modulo.create'));
$response->assertStatus(403);
}
public function test_store_validates_input()
{
$user = User::factory()->create();
$user->givePermissionTo('modulo_create');
$response = $this->actingAs($user)->post(route('modulo.store'), []);
$response->assertSessionHasErrors(['nome', 'email']);
}
}
```
### **Logging e Debug**
```php
// Nel controller
use Illuminate\Support\Facades\Log;
public function action()
{
Log::info('Modulo: Inizio azione', ['user' => auth()->id()]);
try {
// Logica
Log::debug('Modulo: Dati elaborati', ['data' => $processedData]);
} catch (\Exception $e) {
Log::error('Modulo: Errore azione', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'user' => auth()->id()
]);
}
}
```
---
## 💰 **MODELLO BUSINESS E PRICING**
### **Categorie di Moduli**
1. **Gratuiti** (€0):
- Moduli base/community
- Funzionalità essenziali
2. **Standard** (€49-99/anno):
- Moduli specializzati
- Funzionalità avanzate
3. **Premium** (€199-299/anno):
- Moduli complessi
- Integrazione esterna
- Support prioritario
4. **Enterprise** (€499+/anno):
- Moduli mission-critical
- Customizzazione
- SLA garantiti
### **Licenze e Distribuzione**
```json
{
"license": "commercial",
"price": 99.00,
"currency": "EUR",
"billing": "yearly",
"trial_days": 30,
"support_included": true,
"updates_included": true,
"installation_limit": 1
}
```
---
## 🚀 **DEPLOYMENT E MANUTENZIONE**
### **Checklist Pre-Release**
- [ ] Test su ambiente locale
- [ ] Validazione sicurezza
- [ ] Performance test
- [ ] Documentazione completa
- [ ] Screenshot/demo
- [ ] Pricing definito
- [ ] Support plan
### **Processo di Aggiornamento**
```php
// Migration di aggiornamento
public function up(): void
{
// Verifica versione precedente
$currentVersion = DB::table('modules')
->where('name', 'NomeModulo')
->value('version');
if (version_compare($currentVersion, '1.1.0', '<')) {
// Modifiche per v1.1.0
Schema::table('modulo_table', function (Blueprint $table) {
$table->string('nuovo_campo')->nullable();
});
}
}
```
---
## 📚 **RISORSE E SUPPORTO**
### **Documentazione Obbligatoria**
1. **README.md** - Panoramica e installazione
2. **CHANGELOG.md** - Log delle modifiche
3. **API.md** - Documentazione API se presente
4. **HELP.md** - Guida utente
### **Support e Community**
- Forum sviluppatori NetGescon
- Documentation wiki
- Code repository template
- Issue tracking system
---
## ✅ **CHECKLIST MODULO COMPLETO**
### **Files Essenziali**
- [ ] `module.json` configurato
- [ ] Controller con CRUD base
- [ ] Route definitions
- [ ] Migration database
- [ ] Seeder con dati base
- [ ] Template Blade base
- [ ] Middleware permessi
- [ ] Hook integration
- [ ] Unit tests
- [ ] Documentazione
### **Funzionalità Base**
- [ ] Autenticazione/Autorizzazione
- [ ] CRUD operations
- [ ] Ricerca e filtri
- [ ] Paginazione
- [ ] Export dati
- [ ] Logging attività
- [ ] Cache optimization
- [ ] Error handling
- [ ] Input validation
- [ ] API endpoints
### **Qualità Codice**
- [ ] PSR-4 compliance
- [ ] Commenti e documentation
- [ ] Error handling robusto
- [ ] Security best practices
- [ ] Performance optimization
- [ ] Mobile responsive UI
- [ ] Cross-browser compatibility
- [ ] Accessibility standards
---
**🎯 CONCLUSIONE**
Questa guida fornisce tutto il necessario per sviluppare moduli professionali per NetGescon. Il sistema modulare è progettato per essere:
- **Scalabile**: Supporta crescita dell'applicazione
- **Sicuro**: Permessi granulari e validazione
- **Performante**: Cache e ottimizzazioni database
- **Manutenibile**: Codice pulito e documentato
- **Commerciale**: Modello business integrato
Utilizzando questa specifica, puoi sviluppare moduli che si integrano perfettamente con NetGescon e possono essere distribuiti come prodotti commerciali.
---
**📞 CONTATTI SVILUPPO**
- Team NetGescon
- Email: dev@netgescon.com
- Repository: https://github.com/netgescon/modules