1162 lines
34 KiB
Markdown
1162 lines
34 KiB
Markdown
# 📋 GUIDA COMPLETA SVILUPPO MODULI NETGESCON
|
|
**Versione: 2.0 - Data: 01 Agosto 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.
|
|
|
|
**✅ AGGIORNAMENTI VERSIONE 2.0:**
|
|
- Layout template unificato (`modules.layouts.base`)
|
|
- Sistema permessi automatico migliorato
|
|
- Integrazione grafica perfetta con NetGescon
|
|
- Sidebar dinamica con stati moduli
|
|
- Cache management ottimizzato
|
|
|
|
### **🏗️ ARCHITETTURA MODULARE**
|
|
|
|
```
|
|
NetGescon/
|
|
├── app/
|
|
│ ├── Services/
|
|
│ │ ├── ModuleManager.php # Gestione moduli
|
|
│ │ └── HookManager.php # Sistema eventi
|
|
│ ├── Providers/
|
|
│ │ └── ModuleServiceProvider.php # Caricamento automatico moduli
|
|
│ └── Modules/
|
|
│ └── [NomeModulo]/
|
|
│ ├── module.json # Configurazione modulo
|
|
│ ├── Controllers/ # Controller Laravel
|
|
│ ├── Routes/ # File route
|
|
│ ├── Migrations/ # Migrazioni database
|
|
│ ├── Seeders/ # Dati iniziali
|
|
│ └── Views/ # Template Blade
|
|
├── resources/views/
|
|
│ └── modules/
|
|
│ └── layouts/
|
|
│ └── base.blade.php # Template base moduli
|
|
└── docs/
|
|
├── SPECIFICA-SVILUPPO-MODULI.md
|
|
└── TEMPLATE-SVILUPPO-MODULI.md
|
|
```
|
|
|
|
---
|
|
|
|
## 🎨 **NUOVO SISTEMA LAYOUT UNIFICATO**
|
|
|
|
### **Template Base Standard**
|
|
|
|
Tutti i moduli DEVONO utilizzare il template base unificato:
|
|
|
|
```blade
|
|
@extends('modules.layouts.base')
|
|
|
|
@php
|
|
$moduleTitle = 'Nome del Modulo';
|
|
$moduleIcon = 'fas fa-calculator';
|
|
$moduleBreadcrumb = 'Moduli > Nome Modulo';
|
|
@endphp
|
|
|
|
@section('module-actions')
|
|
<div class="btn-group" role="group">
|
|
@can('modulo_create')
|
|
<a href="{{ route('modulo.create') }}" class="btn btn-module-primary">
|
|
<i class="fas fa-plus me-1"></i>
|
|
Nuovo Elemento
|
|
</a>
|
|
@endcan
|
|
<button type="button" class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown">
|
|
<i class="fas fa-cog"></i>
|
|
</button>
|
|
<ul class="dropdown-menu">
|
|
<li><a class="dropdown-item" href="#"><i class="fas fa-file-export me-2"></i>Esporta</a></li>
|
|
<li><a class="dropdown-item" href="#"><i class="fas fa-cog me-2"></i>Impostazioni</a></li>
|
|
</ul>
|
|
</div>
|
|
@endsection
|
|
|
|
@section('module-content')
|
|
<!-- Il tuo contenuto qui -->
|
|
@endsection
|
|
```
|
|
|
|
### **Classi CSS Standardizzate**
|
|
|
|
Il sistema fornisce queste classi CSS preconfigurate:
|
|
|
|
```css
|
|
/* Card statistiche */
|
|
.module-stats-card /* Card per statistiche rapide */
|
|
.module-stats-number /* Numero grande nelle statistiche */
|
|
.module-stats-label /* Label sotto il numero */
|
|
|
|
/* Pulsanti */
|
|
.btn-module-primary /* Pulsante primario modulo */
|
|
|
|
/* Layout */
|
|
.module-card /* Card generica per contenuti */
|
|
.module-card-header /* Header della card */
|
|
.module-card-body /* Body della card */
|
|
.module-card-title /* Titolo della card */
|
|
.module-empty-state /* Stato vuoto con icona */
|
|
```
|
|
|
|
---
|
|
|
|
## 🔐 **NUOVO SISTEMA PERMESSI AUTOMATICO**
|
|
|
|
### **Comando di Installazione Permessi**
|
|
|
|
Ogni modulo deve avere un comando Artisan per installare i permessi:
|
|
|
|
```php
|
|
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use Illuminate\Console\Command;
|
|
use Spatie\Permission\Models\Permission;
|
|
use Spatie\Permission\Models\Role;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class UpdateNomeModuloPermissions extends Command
|
|
{
|
|
protected $signature = 'nomemodulo:permissions';
|
|
protected $description = 'Installa i permessi per il modulo NomeModulo';
|
|
|
|
public function handle()
|
|
{
|
|
$this->info('🔧 Installazione permessi modulo NomeModulo...');
|
|
|
|
// Definisci i permessi del modulo
|
|
$permissions = [
|
|
'nomemodulo_view' => 'Visualizzare elementi del modulo',
|
|
'nomemodulo_create' => 'Creare nuovi elementi',
|
|
'nomemodulo_edit' => 'Modificare elementi esistenti',
|
|
'nomemodulo_delete' => 'Eliminare elementi',
|
|
'nomemodulo_export' => 'Esportare dati',
|
|
'nomemodulo_settings' => 'Gestire impostazioni modulo'
|
|
];
|
|
|
|
// Crea i permessi
|
|
foreach ($permissions as $name => $description) {
|
|
$permission = Permission::firstOrCreate(
|
|
['name' => $name],
|
|
['guard_name' => 'web']
|
|
);
|
|
$this->line("✅ Permesso: {$name}");
|
|
}
|
|
|
|
// Assegna permessi ai ruoli amministrativi
|
|
$adminRoles = ['admin', 'amministratore', 'super-admin'];
|
|
|
|
foreach ($adminRoles as $roleName) {
|
|
$role = Role::where('name', $roleName)->first();
|
|
if ($role) {
|
|
foreach (array_keys($permissions) as $permissionName) {
|
|
$permission = Permission::where('name', $permissionName)->first();
|
|
if ($permission && !$role->hasPermissionTo($permission)) {
|
|
$role->givePermissionTo($permission);
|
|
}
|
|
}
|
|
$this->line("✅ Permessi assegnati al ruolo: {$roleName}");
|
|
}
|
|
}
|
|
|
|
// Aggiorna il record del modulo nel database
|
|
DB::table('modules')
|
|
->where('name', 'NomeModulo')
|
|
->update([
|
|
'permissions' => json_encode(array_keys($permissions)),
|
|
'updated_at' => now()
|
|
]);
|
|
|
|
$this->info('🎉 Permessi modulo NomeModulo installati con successo!');
|
|
return 0;
|
|
}
|
|
}
|
|
```
|
|
|
|
### **Utilizzo Permessi nei Controller**
|
|
|
|
```php
|
|
public function __construct()
|
|
{
|
|
$this->middleware('auth');
|
|
$this->middleware('permission:nomemodulo_view')->only(['index', 'show']);
|
|
$this->middleware('permission:nomemodulo_create')->only(['create', 'store']);
|
|
$this->middleware('permission:nomemodulo_edit')->only(['edit', 'update']);
|
|
$this->middleware('permission:nomemodulo_delete')->only(['destroy']);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 **SIDEBAR DINAMICA E STATI MODULI**
|
|
|
|
### **Integrazione nella Sidebar**
|
|
|
|
Il sistema gestisce automaticamente l'apparizione dei moduli nella sidebar. La sidebar mostra:
|
|
|
|
- ✅ **ATTIVO** - Modulo installato e funzionante
|
|
- ❌ **Prezzo** - Modulo disponibile ma non installato
|
|
- 🔒 **Disabilitato** - Modulo installato ma non attivo
|
|
|
|
### **Aggiornamento SidebarComposer**
|
|
|
|
Il `SidebarComposer` è stato aggiornato per fornire la lista `activeModules`:
|
|
|
|
```php
|
|
public function compose(View $view)
|
|
{
|
|
// ...codice esistente...
|
|
|
|
$view->with([
|
|
'stabili' => $stabili,
|
|
'stabileAttivo' => $stabileAttivo,
|
|
'anni' => $gestioni->pluck('anno_gestione')->unique(),
|
|
'annoAttivo' => $annoAttivo,
|
|
'gestione' => $gestioneAttiva,
|
|
'activeModules' => Cache::remember('netgescon.active_modules', 3600, function() {
|
|
return DB::table('modules')->where('status', 'active')->pluck('name')->toArray();
|
|
}),
|
|
]);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🚀 **PROCESSO DI INSTALLAZIONE SEMPLIFICATO**
|
|
|
|
### **1. Registrazione Database**
|
|
|
|
```php
|
|
// Via Tinker o comando
|
|
DB::table('modules')->insert([
|
|
'name' => 'NomeModulo',
|
|
'display_name' => 'Nome Visualizzato',
|
|
'description' => 'Descrizione del modulo',
|
|
'version' => '1.0.0',
|
|
'status' => 'active',
|
|
'license' => 'commercial',
|
|
'price' => 99.00,
|
|
'currency' => 'EUR',
|
|
'billing' => 'yearly',
|
|
'config' => '{}',
|
|
'dependencies' => '[]',
|
|
'permissions' => '[]',
|
|
'created_at' => now(),
|
|
'updated_at' => now()
|
|
]);
|
|
```
|
|
|
|
### **2. Installazione Permessi**
|
|
|
|
```bash
|
|
php artisan nomemodulo:permissions
|
|
```
|
|
|
|
### **3. Pulizia Cache**
|
|
|
|
```bash
|
|
php artisan cache:clear
|
|
php artisan config:clear
|
|
php artisan view:clear
|
|
```
|
|
|
|
---
|
|
|
|
## ✅ **CHECKLIST MODULO COMPLETO V2.0**
|
|
|
|
### **Files Obbligatori**
|
|
- [ ] Controller con CRUD base
|
|
- [ ] Route definitions (`Routes/web.php`)
|
|
- [ ] Migration database
|
|
- [ ] Views usando `@extends('modules.layouts.base')`
|
|
- [ ] Comando permessi (`php artisan make:command UpdateNomeModuloPermissions`)
|
|
- [ ] Middleware permessi nel controller
|
|
- [ ] Integrazione nella sidebar
|
|
|
|
### **Aspetti Tecnici**
|
|
- [ ] Utilizza template base unificato
|
|
- [ ] Permessi installati e assegnati
|
|
- [ ] Cache management corretto
|
|
- [ ] Responsive design (Bootstrap 5)
|
|
- [ ] Tooltips e UX enhancements
|
|
- [ ] Error handling robusto
|
|
- [ ] Empty states informativi
|
|
|
|
### **Testing**
|
|
- [ ] Modulo visibile in sidebar
|
|
- [ ] Permessi funzionanti (403 risolto)
|
|
- [ ] Layout grafico coerente
|
|
- [ ] Funzionalità CRUD complete
|
|
- [ ] Performance accettabili
|
|
|
|
---
|
|
|
|
## 🎯 **ESEMPIO PRATICO: MODULO CONTABILITÀ**
|
|
|
|
Il modulo Contabilità è il nostro modulo di riferimento che implementa tutte le best practices:
|
|
|
|
### **Struttura File**
|
|
```
|
|
app/Modules/Contabilita/
|
|
├── Controllers/ContabilitaController.php
|
|
├── Routes/web.php
|
|
├── Migrations/2025_01_22_170000_create_piano_conti_tables.php
|
|
└── Views/
|
|
└── index.blade.php
|
|
```
|
|
|
|
### **Features Implementate**
|
|
- ✅ Layout unificato con template base
|
|
- ✅ Statistiche rapide in card
|
|
- ✅ Tabella responsive con azioni
|
|
- ✅ Filtri di ricerca avanzati
|
|
- ✅ Sistema permessi completo
|
|
- ✅ Empty state informativo
|
|
- ✅ Pagination integrata
|
|
- ✅ Tooltips e UX enhancements
|
|
|
|
---
|
|
|
|
## 📞 **SUPPORTO E RISORSE**
|
|
|
|
### **Documentazione di Riferimento**
|
|
- `docs/SPECIFICA-SVILUPPO-MODULI.md` - Questa guida
|
|
- `docs/TEMPLATE-SVILUPPO-MODULI.md` - Template da copiare
|
|
- `resources/views/modules/layouts/base.blade.php` - Template base
|
|
|
|
### **Moduli di Esempio**
|
|
- **Contabilità** - Modulo complesso con statistiche e CRUD
|
|
- **StampeRate** - Modulo con processing e file generation
|
|
|
|
### **Comandi Utili**
|
|
```bash
|
|
# Installa permessi modulo
|
|
php artisan nomemodulo:permissions
|
|
|
|
# Controlla moduli attivi
|
|
php artisan tinker --execute="DB::table('modules')->where('status', 'active')->get(['name', 'status'])"
|
|
|
|
# Pulisci cache moduli
|
|
php artisan cache:clear
|
|
```
|
|
|
|
---
|
|
|
|
**✨ Il sistema modulare NetGescon è ora maturo, stabile e facilmente estendibile. Seguendo questa guida potrai creare moduli professionali che si integrano perfettamente con l'ecosistema esistente.**
|
|
|
|
---
|
|
|
|
## 🎯 **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
|