netgescon-day0/docs/90-UI-interfaccia-unica/90-01-content/04-controller-standard.md

17 KiB

📊 CONTROLLER STANDARD E DATABASE

Pattern controller per moduli NetGescon


🎯 CONTROLLER TEMPLATE BASE

File: app/Http/Controllers/Admin/{Modulo}Controller.php

<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;

class ModuloController extends Controller
{
    /**
     * Display main listing with stats and menu
     */
    public function index(Request $request)
    {
        // Calcola statistiche per cards
        $stats = $this->calculateStats();
        
        // Dati principale per listing con filtri
        $items = $this->getMainData($request);
        
        // Menu configuration
        $menuTabs = $this->getMenuTabs();
        
        return view('admin.modulo.index', compact('stats', 'items', 'menuTabs'));
    }
    
    /**
     * Show detail with sub-tabs
     */
    public function show($id)
    {
        $item = $this->findItem($id);
        $detailTabs = $this->getDetailTabs($item);
        $tabContent = $this->getTabContent($item);
        
        return view('admin.modulo.show', compact('item', 'detailTabs', 'tabContent'));
    }
    
    /**
     * Show create form
     */
    public function create()
    {
        $formData = $this->getFormData();
        return view('admin.modulo.create', compact('formData'));
    }
    
    /**
     * Store new item
     */
    public function store(Request $request)
    {
        $validatedData = $request->validate($this->getValidationRules());
        
        // Add audit fields
        $validatedData['created_by'] = Auth::id();
        $validatedData['amministratore_id'] = Auth::user()->amministratore_id;
        
        $item = DB::table('modulo_table')->insertGetId($validatedData);
        
        return redirect()->route('admin.modulo.index')
            ->with('success', 'Item creato con successo');
    }
    
    /**
     * Show edit form
     */
    public function edit($id)
    {
        $item = $this->findItem($id);
        $formData = $this->getFormData();
        
        return view('admin.modulo.edit', compact('item', 'formData'));
    }
    
    /**
     * Update item
     */
    public function update(Request $request, $id)
    {
        $validatedData = $request->validate($this->getValidationRules());
        
        // Add audit fields
        $validatedData['updated_by'] = Auth::id();
        $validatedData['updated_at'] = now();
        
        DB::table('modulo_table')
            ->where('id', $id)
            ->where('amministratore_id', Auth::user()->amministratore_id)
            ->update($validatedData);
        
        return redirect()->route('admin.modulo.index')
            ->with('success', 'Item aggiornato con successo');
    }
    
    /**
     * Delete item
     */
    public function destroy($id)
    {
        DB::table('modulo_table')
            ->where('id', $id)
            ->where('amministratore_id', Auth::user()->amministratore_id)
            ->delete();
        
        return redirect()->route('admin.modulo.index')
            ->with('success', 'Item eliminato con successo');
    }
    
    /**
     * Reports page
     */
    public function reports()
    {
        $reportData = $this->getReportData();
        return view('admin.modulo.reports', compact('reportData'));
    }
    
    /**
     * Configuration page
     */
    public function config()
    {
        $configData = $this->getConfigData();
        return view('admin.modulo.config', compact('configData'));
    }
    
    // ============ PRIVATE METHODS ============
    
    /**
     * Calculate statistics for cards
     */
    private function calculateStats()
    {
        $baseQuery = DB::table('modulo_table')
            ->when(Auth::user()->amministratore_id, function($query) {
                return $query->where('amministratore_id', Auth::user()->amministratore_id);
            });
        
        return [
            'totale' => (clone $baseQuery)->count(),
            'attivi' => (clone $baseQuery)->where('stato', 'attivo')->count(),
            'pending' => (clone $baseQuery)->where('stato', 'pending')->count(),
            'questo_mese' => (clone $baseQuery)
                ->whereMonth('created_at', now()->month)
                ->whereYear('created_at', now()->year)
                ->count(),
        ];
    }
    
    /**
     * Get main data with relations and filters
     */
    private function getMainData(Request $request)
    {
        $query = DB::table('modulo_table')
            ->leftJoin('stabili', 'modulo_table.stabile_id', '=', 'stabili.id')
            ->leftJoin('gestioni', 'modulo_table.gestione_id', '=', 'gestioni.id_gestione')
            ->select([
                'modulo_table.*',
                'stabili.denominazione as stabile_nome',
                'gestioni.anno_gestione',
                'gestioni.tipo_gestione'
            ])
            ->when(Auth::user()->amministratore_id, function($query) {
                return $query->where('stabili.amministratore_id', Auth::user()->amministratore_id);
            });
        
        // Apply filters
        if ($request->filled('search')) {
            $query->where(function($q) use ($request) {
                $q->where('modulo_table.denominazione', 'like', '%' . $request->search . '%')
                  ->orWhere('stabili.denominazione', 'like', '%' . $request->search . '%');
            });
        }
        
        if ($request->filled('stato')) {
            $query->where('modulo_table.stato', $request->stato);
        }
        
        if ($request->filled('stabile_id')) {
            $query->where('modulo_table.stabile_id', $request->stabile_id);
        }
        
        return $query->orderBy('modulo_table.created_at', 'desc')->paginate(20);
    }
    
    /**
     * Find single item with security check
     */
    private function findItem($id)
    {
        $item = DB::table('modulo_table')
            ->leftJoin('stabili', 'modulo_table.stabile_id', '=', 'stabili.id')
            ->select([
                'modulo_table.*',
                'stabili.denominazione as stabile_nome'
            ])
            ->where('modulo_table.id', $id)
            ->when(Auth::user()->amministratore_id, function($query) {
                return $query->where('stabili.amministratore_id', Auth::user()->amministratore_id);
            })
            ->first();
        
        if (!$item) {
            abort(404, 'Item non trovato');
        }
        
        return $item;
    }
    
    /**
     * Get menu tabs configuration
     */
    private function getMenuTabs()
    {
        return [
            [
                'label' => 'Lista',
                'route' => 'admin.modulo.index',
                'icon' => 'fas fa-list',
                'active' => request()->routeIs('admin.modulo.index')
            ],
            [
                'label' => 'Nuovo',
                'route' => 'admin.modulo.create',
                'icon' => 'fas fa-plus',
                'active' => request()->routeIs('admin.modulo.create')
            ],
            [
                'label' => 'Reports',
                'route' => 'admin.modulo.reports',
                'icon' => 'fas fa-chart-bar',
                'active' => request()->routeIs('admin.modulo.reports')
            ],
            [
                'label' => 'Config',
                'route' => 'admin.modulo.config',
                'icon' => 'fas fa-cog',
                'active' => request()->routeIs('admin.modulo.config')
            ],
        ];
    }
    
    /**
     * Get detail tabs for show page
     */
    private function getDetailTabs($item)
    {
        return [
            [
                'id' => 'dati-generali',
                'label' => 'Dati Generali',
                'icon' => 'fas fa-info-circle',
                'active' => true
            ],
            [
                'id' => 'dettagli',
                'label' => 'Dettagli',
                'icon' => 'fas fa-list',
                'active' => false
            ],
            [
                'id' => 'documenti',
                'label' => 'Documenti',
                'icon' => 'fas fa-file',
                'active' => false
            ],
        ];
    }
    
    /**
     * Get validation rules
     */
    private function getValidationRules()
    {
        return [
            'denominazione' => 'required|string|max:255',
            'descrizione' => 'nullable|string',
            'stato' => 'required|in:attivo,inattivo,sospeso',
            'stabile_id' => 'nullable|exists:stabili,id',
            'gestione_id' => 'nullable|exists:gestioni,id_gestione',
        ];
    }
    
    /**
     * Get form data for create/edit
     */
    private function getFormData()
    {
        return [
            'stabili' => DB::table('stabili')
                ->when(Auth::user()->amministratore_id, function($query) {
                    return $query->where('amministratore_id', Auth::user()->amministratore_id);
                })
                ->select('id', 'denominazione')
                ->orderBy('denominazione')
                ->get(),
            'gestioni' => DB::table('gestioni')
                ->join('stabili', 'gestioni.stabile_id', '=', 'stabili.id')
                ->when(Auth::user()->amministratore_id, function($query) {
                    return $query->where('stabili.amministratore_id', Auth::user()->amministratore_id);
                })
                ->select('gestioni.id_gestione', 'gestioni.anno_gestione', 'stabili.denominazione as stabile_nome')
                ->orderBy('gestioni.anno_gestione', 'desc')
                ->get(),
            'stati' => [
                'attivo' => 'Attivo',
                'inattivo' => 'Inattivo',
                'sospeso' => 'Sospeso'
            ]
        ];
    }
    
    /**
     * Get tab content for show page
     */
    private function getTabContent($item)
    {
        return [
            'dati_generali' => $item,
            'dettagli' => $this->getDetailData($item->id),
            'documenti' => $this->getDocumenti($item->id),
        ];
    }
    
    /**
     * Get additional detail data
     */
    private function getDetailData($itemId)
    {
        // Get related data for details tab
        return [];
    }
    
    /**
     * Get documents for item
     */
    private function getDocumenti($itemId)
    {
        // Get documents related to this item
        return [];
    }
    
    /**
     * Get report data
     */
    private function getReportData()
    {
        // Generate report data
        return [];
    }
    
    /**
     * Get configuration data
     */
    private function getConfigData()
    {
        // Get configuration settings
        return [];
    }
}

🔐 SECURITY PATTERNS

Filtro Amministratore Obbligatorio:

// SEMPRE filtrare per amministratore
private function getBaseQuery()
{
    return DB::table('modulo_table')
        ->when(Auth::user()->amministratore_id, function($query) {
            return $query->where('amministratore_id', Auth::user()->amministratore_id);
        });
}

// Per super admin senza filtro
private function isSuperAdmin()
{
    return Auth::user()->is_super_admin ?? false;
}

Validation con Security:

private function getValidationRules($id = null)
{
    return [
        'denominazione' => 'required|string|max:255',
        'stabile_id' => [
            'nullable',
            'exists:stabili,id',
            // Verifica che lo stabile appartenga all'amministratore
            function ($attribute, $value, $fail) {
                if ($value && Auth::user()->amministratore_id) {
                    $exists = DB::table('stabili')
                        ->where('id', $value)
                        ->where('amministratore_id', Auth::user()->amministratore_id)
                        ->exists();
                    if (!$exists) {
                        $fail('Stabile non autorizzato.');
                    }
                }
            },
        ],
    ];
}

📊 DATABASE QUERY PATTERNS

Query con Relazioni Standard:

// Pattern base per listing
private function getItemsWithRelations()
{
    return DB::table('modulo_table as m')
        ->leftJoin('stabili as s', 'm.stabile_id', '=', 's.id')
        ->leftJoin('gestioni as g', 'm.gestione_id', '=', 'g.id_gestione')
        ->leftJoin('unita_immobiliari as ui', 'm.unita_id', '=', 'ui.id')
        ->select([
            'm.*',
            's.denominazione as stabile_nome',
            's.indirizzo as stabile_indirizzo',
            'g.anno_gestione',
            'g.tipo_gestione',
            'ui.denominazione as unita_nome'
        ])
        ->where('s.amministratore_id', Auth::user()->amministratore_id);
}

Stats Query Ottimizzate:

private function calculateAdvancedStats()
{
    $amministratore_id = Auth::user()->amministratore_id;
    
    // Single query con aggregazione
    $stats = DB::table('modulo_table as m')
        ->join('stabili as s', 'm.stabile_id', '=', 's.id')
        ->where('s.amministratore_id', $amministratore_id)
        ->selectRaw('
            COUNT(*) as totale,
            SUM(CASE WHEN m.stato = "attivo" THEN 1 ELSE 0 END) as attivi,
            SUM(CASE WHEN m.stato = "pending" THEN 1 ELSE 0 END) as pending,
            SUM(CASE WHEN MONTH(m.created_at) = MONTH(NOW()) 
                     AND YEAR(m.created_at) = YEAR(NOW()) THEN 1 ELSE 0 END) as questo_mese,
            AVG(m.importo) as importo_medio,
            SUM(m.importo) as importo_totale
        ')
        ->first();
    
    return (array) $stats;
}

🚀 ROUTE CONFIGURATION

File: routes/admin.php

Route::prefix('admin')->name('admin.')->middleware(['auth'])->group(function () {
    
    // Standard RESTful routes
    Route::resource('modulo', 'ModuloController');
    
    // Additional custom routes
    Route::get('modulo/{id}/detail', ['ModuloController', 'detail'])->name('modulo.detail');
    Route::get('modulo/reports/index', ['ModuloController', 'reports'])->name('modulo.reports');
    Route::get('modulo/config/index', ['ModuloController', 'config'])->name('modulo.config');
    
    // AJAX routes
    Route::post('modulo/{id}/toggle-status', ['ModuloController', 'toggleStatus'])->name('modulo.toggle-status');
    Route::get('modulo/search/autocomplete', ['ModuloController', 'autocomplete'])->name('modulo.autocomplete');
});

📝 EXAMPLE IMPLEMENTATIONS

Fornitori Controller:

class FornitoriController extends Controller
{
    private function calculateStats()
    {
        return [
            'totale_fornitori' => DB::table('fornitori')->count(),
            'attivi' => DB::table('fornitori')->where('stato', 'attivo')->count(),
            'questo_mese' => DB::table('fornitori')
                ->whereMonth('created_at', now()->month)->count(),
            'fatture_pending' => DB::table('fatture_fornitori')
                ->where('stato', 'pending')->count(),
        ];
    }
    
    private function getValidationRules()
    {
        return [
            'denominazione' => 'required|string|max:255',
            'partita_iva' => 'required|string|max:20',
            'codice_fiscale' => 'nullable|string|max:16',
            'email' => 'nullable|email',
            'telefono' => 'nullable|string|max:20',
            'indirizzo' => 'nullable|string',
            'categoria' => 'required|in:manutenzione,pulizie,servizi,altro',
            'stato' => 'required|in:attivo,inattivo,sospeso',
        ];
    }
}

Gestioni Controller:

class GestioniController extends Controller
{
    private function calculateStats()
    {
        return [
            'totale_gestioni' => DB::table('gestioni')->count(),
            'aperte' => DB::table('gestioni')->where('stato', 'aperta')->count(),
            'da_chiudere' => DB::table('gestioni')
                ->where('data_fine', '<=', now())
                ->where('stato', 'aperta')->count(),
            'archiviate' => DB::table('gestioni')->where('stato', 'archiviata')->count(),
        ];
    }
}

PERFORMANCE TIPS

Query Optimization:

// Use indexes
private function addIndexes()
{
    // In migration
    $table->index(['amministratore_id', 'stato']);
    $table->index(['stabile_id', 'created_at']);
    $table->index('stato');
}

// Limit data
private function getMainData($request)
{
    return $this->getBaseQuery()
        ->select([
            'm.id', 'm.denominazione', 'm.stato', 'm.created_at',
            's.denominazione as stabile_nome'
        ]) // Solo campi necessari
        ->paginate(20); // Limitare risultati
}

Caching Stats:

private function calculateStats()
{
    return Cache::remember(
        'stats_modulo_' . Auth::user()->amministratore_id,
        now()->addMinutes(5),
        function () {
            return $this->getStatsFromDatabase();
        }
    );
}

📖 Prossimo file: 05-css-javascript.md