# π 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 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 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 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 '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') Nuovo @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')