++ Primo Caricamento

This commit is contained in:
2026-03-30 19:15:13 +02:00
commit 663a68d59b
47 changed files with 3561 additions and 0 deletions

578
README.md Normal file
View File

@@ -0,0 +1,578 @@
# Portale Clienti — Applicazione Laravel 11 Demo
Applicazione di esempio per la gestione clienti costruita con **Laravel 11** e **Docker**.
È pensata come punto di partenza e materiale didattico per chi si avvicina a Laravel per la prima volta.
---
## Indice
1. [Cos'è questo progetto](#cosè-questo-progetto)
2. [Pre-requisiti](#pre-requisiti)
3. [Avvio rapido](#avvio-rapido)
4. [Struttura del progetto](#struttura-del-progetto)
5. [Architettura Docker](#architettura-docker)
6. [Concetti Laravel spiegati](#concetti-laravel-spiegati)
7. [Configurazione: .env vs Impostazioni dinamiche](#configurazione-env-vs-impostazioni-dinamiche)
8. [CRUD Clienti — come funziona](#crud-clienti--come-funziona)
9. [Comandi utili](#comandi-utili)
10. [Workflow di sviluppo](#workflow-di-sviluppo)
11. [Prossimi passi](#prossimi-passi)
---
## Cos'è questo progetto
Una piccola applicazione web che permette di:
- **Visualizzare una dashboard** con statistiche sui clienti
- **Gestire clienti** — creare, modificare, consultare, eliminare (CRUD completo)
- **Configurare l'applicazione** tramite un pannello impostazioni, senza toccare il codice
Le funzionalità sono intenzionalmente semplici: l'obiettivo è mostrare i pattern fondamentali di Laravel in modo chiaro.
---
## Pre-requisiti
Installa questi strumenti **prima** di iniziare:
| Strumento | Versione minima | Link |
|-----------|-----------------|------|
| Docker Desktop | 4.x | https://www.docker.com/products/docker-desktop |
| Git | 2.x | https://git-scm.com |
> **Non serve** installare PHP, Composer o MySQL sul tuo computer. Docker gestisce tutto.
---
## Avvio rapido
```bash
# 1. Clona il repository
git clone <url-repo> portale-clienti
cd portale-clienti
# 2. Copia il file di configurazione
cp .env.example .env
# Modifica .env se necessario (le password predefinite vanno bene per lo sviluppo locale)
# 3. Costruisci e avvia i container Docker
docker compose up -d --build
# ✅ L'applicazione sarà disponibile su http://localhost:8080
```
> Al primo avvio, Docker:
> 1. Costruisce l'immagine PHP con tutte le dipendenze
> 2. Esegue `composer install` per installare Laravel e le librerie
> 3. Genera l'`APP_KEY` crittografica
> 4. Esegue le migration (crea le tabelle nel database)
> 5. Popola il database con dati di esempio (clienti fittizi e impostazioni di default)
---
## Struttura del progetto
```
portale-clienti/
├── app/ ← Codice PHP dell'applicazione
│ ├── Http/
│ │ └── Controllers/ ← Controller: ricevono richieste HTTP
│ │ ├── DashboardController.php
│ │ ├── CustomerController.php
│ │ └── SettingController.php
│ ├── Models/ ← Model: rappresentano i dati (tabelle DB)
│ │ ├── Customer.php
│ │ └── Setting.php
│ ├── Providers/
│ │ └── AppServiceProvider.php ← Configurazione avvio applicazione
│ └── Services/
│ └── SettingService.php ← Logica di business per le impostazioni
├── bootstrap/
│ └── app.php ← Punto di bootstrap Laravel 11
├── config/ ← File di configurazione
│ ├── app.php ← Config app (nome, chiave, timezone...)
│ ├── database.php ← Config connessioni DB
│ └── settings.php ← ⭐ Config impostazioni dinamiche (vedi sotto)
├── database/
│ ├── migrations/ ← Definizioni schema database
│ │ ├── 2024_01_01_000010_create_customers_table.php
│ │ └── 2024_01_01_000020_create_settings_table.php
│ └── seeders/ ← Dati iniziali
│ ├── DatabaseSeeder.php
│ ├── SettingSeeder.php
│ └── CustomerSeeder.php
├── docker/
│ ├── nginx/
│ │ └── default.conf ← Configurazione web server
│ └── php/
│ ├── Dockerfile ← Immagine PHP custom
│ ├── entrypoint.sh ← Script avvio container
│ └── php.ini ← Configurazione PHP
├── public/
│ └── index.php ← Front controller (unico file esposto al web)
├── resources/
│ └── views/ ← Template HTML (Blade)
│ ├── layouts/
│ │ └── app.blade.php ← Layout principale condiviso
│ ├── dashboard.blade.php
│ ├── customers/
│ │ ├── index.blade.php
│ │ ├── create.blade.php
│ │ ├── edit.blade.php
│ │ ├── show.blade.php
│ │ └── _form.blade.php ← Partial riutilizzato da create ed edit
│ └── settings/
│ └── index.blade.php
├── routes/
│ ├── web.php ← Route HTTP (URL → Controller)
│ └── console.php ← Comandi Artisan e scheduler
├── storage/ ← File generati (log, cache, sessioni, upload)
├── .env ← ⭐ Variabili statiche (non committare!)
├── .env.example ← Template pubblico del .env
├── composer.json ← Dipendenze PHP
└── docker-compose.yml ← Orchestrazione container
```
---
## Architettura Docker
```
┌─────────────────────────────────────────────┐
│ docker-compose.yml │
│ │
Browser ──────► │ [Nginx :8080] ──► [PHP-FPM :9000] │
:8080 │ webserver app │
│ │ │
│ [MySQL :3306] │
│ db │
│ [Redis :6379] │
│ redis │
└─────────────────────────────────────────────┘
```
### I 4 container
| Container | Immagine | Ruolo |
|-----------|----------|-------|
| `portale_nginx` | nginx:1.25-alpine | Web server. Serve file statici e passa le richieste PHP a FPM |
| `portale_app` | Custom (basata su php:8.2-fpm) | Esegue il codice PHP Laravel |
| `portale_db` | mysql:8.0 | Database relazionale |
| `portale_redis` | redis:7-alpine | Cache veloce per sessioni e impostazioni |
### Comunicazione tra container
I container si "parlano" usando il **nome del servizio** come hostname.
Per questo in `.env` trovi `DB_HOST=db` (non `localhost`): `db` è il nome del servizio MySQL nel `docker-compose.yml`.
### Volumes (persistenza dati)
```yaml
volumes:
portale_db_data: # I dati MySQL sopravvivono al restart
```
Senza questo volume, ogni `docker compose down` cancella il database.
---
## Concetti Laravel spiegati
### 1. MVC — Model, View, Controller
Laravel usa il pattern **MVC** per separare le responsabilità:
```
Richiesta HTTP
[Route] routes/web.php
│ Mappa URL → Controller
[Controller] app/Http/Controllers/CustomerController.php
│ Riceve la richiesta, chiede i dati al Model, passa tutto alla View
[Model] app/Models/Customer.php
│ Rappresenta la tabella `customers`. Usa Eloquent ORM per le query.
[Database] MySQL
[View] resources/views/customers/index.blade.php
│ Template HTML con variabili PHP. Genera la risposta HTML.
Risposta HTML al browser
```
### 2. Eloquent ORM
Invece di SQL raw, usi metodi PHP fluenti:
```php
// SQL: SELECT * FROM customers WHERE status = 'attivo' ORDER BY name LIMIT 10
$clienti = Customer::where('status', 'attivo')
->orderBy('name')
->take(10)
->get();
// SQL: SELECT * FROM customers WHERE id = 42
$cliente = Customer::find(42);
// SQL: INSERT INTO customers (...) VALUES (...)
$nuovo = Customer::create([
'name' => 'Mario Rossi',
'email' => 'mario@example.com',
// ...
]);
```
### 3. Migration
Le migration sono "versionamento" del database. Invece di scrivere SQL manualmente:
```php
// Crea la tabella customers con colonne ben tipizzate
Schema::create('customers', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamps();
});
```
Comandi:
```bash
php artisan migrate # Esegui migration nuove
php artisan migrate:rollback # Annulla l'ultima batch
php artisan migrate:fresh # ⚠️ Cancella tutto e ricrea (solo in development!)
```
### 4. Blade — Template Engine
Il template engine di Laravel. Sintassi pulita e sicura:
```blade
{{-- Stampa variabile (escape automatico per sicurezza XSS) --}}
{{ $customer->name }}
{{-- Ciclo --}}
@foreach ($customers as $customer)
<tr>...</tr>
@endforeach
{{-- Condizione --}}
@if ($customer->status === 'attivo')
<span class="badge bg-success">Attivo</span>
@endif
{{-- Include partial --}}
@include('customers._form', ['customer' => $customer])
{{-- Eredita layout --}}
@extends('layouts.app')
@section('content')
<!-- contenuto specifico della pagina -->
@endsection
```
### 5. Validazione
Laravel valida i dati dei form in modo dichiarativo:
```php
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:customers',
'type' => 'required|in:privato,azienda',
]);
// Se la validazione fallisce, Laravel torna automaticamente al form
// con gli errori e i valori inseriti (no codice extra necessario)
```
Nelle view, gli errori sono disponibili con `@error('name')`:
```blade
<input type="text" name="name" class="{{ $errors->has('name') ? 'is-invalid' : '' }}">
@error('name')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
```
### 6. Dependency Injection
Laravel può iniettare dipendenze automaticamente nel costruttore dei Controller:
```php
class CustomerController extends Controller
{
// Laravel crea SettingService automaticamente — non serve "new SettingService()"
public function __construct(
private SettingService $settings
) {}
public function index()
{
$perPage = $this->settings->get('items_per_page');
// ...
}
}
```
---
## Configurazione: .env vs Impostazioni dinamiche
Questo progetto separa due tipi di configurazione:
### `.env` — Configurazione STATICA (infrastruttura)
Per valori che **non cambiano nel tempo** o che **variano per ambiente** (locale, staging, produzione):
```env
APP_NAME="Portale Clienti" # Nome app
DB_HOST=db # Host database (nome container Docker)
DB_DATABASE=portale_clienti # Nome database
DB_PASSWORD=portale_secret_pass # Password (diversa per ogni ambiente)
REDIS_HOST=redis # Cache host
```
- Cambiare questi valori richiede il **restart del container**
- **Non si committano su Git** (`.env` è in `.gitignore`)
- Si usa `.env.example` come template pubblico
### `config/settings.php` + tabella `settings` — Configurazione DINAMICA (business)
Per valori che **cambiano nel tempo** e che **l'admin modifica da pannello web**:
```php
// config/settings.php definisce i default e i metadati
'defaults' => [
'items_per_page' => 15, // Righe per pagina
'currency_symbol' => '€', // Simbolo valuta
'welcome_message' => 'Benvenuto!', // Messaggio dashboard
'allow_notes' => true, // Feature flag
]
```
- I valori vengono salvati nella tabella `settings` del database
- Modificabili via interfaccia web (/settings) senza codice o restart
- SettingService li mette in cache Redis per performance
- Ideali per: valute, formati data, messaggi, feature flags, paginazione
### Leggi un'impostazione nel codice
```php
// In un Controller
$symbol = $this->settings->get('currency_symbol', '€');
// In una View (grazie a AppServiceProvider che condivide $appSettings)
{{ $appSettings['currency_symbol'] }}
```
---
## CRUD Clienti — come funziona
### Route Resource
Una sola riga nel file routes genera 7 URL:
```php
Route::resource('customers', CustomerController::class);
```
| Metodo | URL | Azione | Controller |
|--------|-----|--------|------------|
| GET | `/customers` | Lista | `index()` |
| GET | `/customers/create` | Form nuovo | `create()` |
| POST | `/customers` | Salva nuovo | `store()` |
| GET | `/customers/{id}` | Dettaglio | `show()` |
| GET | `/customers/{id}/edit` | Form modifica | `edit()` |
| PUT | `/customers/{id}` | Aggiorna | `update()` |
| DELETE | `/customers/{id}` | Elimina | `destroy()` |
### Soft Delete
I clienti "eliminati" non vengono rimossi fisicamente dal database:
```php
// app/Models/Customer.php
use SoftDeletes; // Aggiunge la colonna deleted_at
// Quando elimini:
$customer->delete();
// → imposta deleted_at = now()
// → NON cancella la riga
// Le query normali ignorano i soft-deleted:
Customer::all() // non include i clienti eliminati
Customer::withTrashed()->find($id) // include anche i soft-deleted
```
---
## Comandi utili
### Container Docker
```bash
# Avvia tutti i container in background
docker compose up -d
# Avvia ricostruendo le immagini (dopo modifiche al Dockerfile)
docker compose up -d --build
# Ferma i container (i dati rimangono)
docker compose stop
# Ferma e rimuove i container (i dati rimangono nel volume)
docker compose down
# Ferma, rimuove container E volumi (⚠️ cancella il database!)
docker compose down -v
# Vedi i log in tempo reale
docker compose logs -f
# Vedi i log solo del container PHP
docker compose logs -f app
```
### Artisan (dentro il container)
```bash
# Entra nel container PHP
docker compose exec app bash
# Da dentro il container, tutti i comandi artisan:
php artisan route:list # Lista tutte le route registrate
php artisan migrate # Esegui migration nuove
php artisan migrate:fresh --seed # Ricrea DB con dati di esempio
php artisan db:seed # Aggiungi solo i dati (senza ricreare tabelle)
php artisan cache:clear # Svuota la cache
php artisan config:clear # Svuota cache configurazione
php artisan tinker # REPL interattivo (prova codice PHP/Laravel)
# Scorciatoia senza entrare nel container:
docker compose exec app php artisan route:list
```
### Tinker — REPL interattivo
Tinker è uno strumento potentissimo per esplorare e testare:
```bash
docker compose exec app php artisan tinker
# Dentro Tinker:
>>> Customer::count() # Quanti clienti ci sono?
>>> Customer::active()->get() # Clienti attivi
>>> Customer::find(1) # Cliente con ID 1
>>> Customer::create(['name' => 'Test', 'email' => 'test@test.it', 'type' => 'azienda', 'status' => 'attivo'])
>>> app(\App\Services\SettingService::class)->get('currency_symbol')
```
---
## Workflow di sviluppo
### Modificare il codice
I file del progetto sono **montati come volume** nel container Docker:
```yaml
volumes:
- .:/var/www # directory locale → /var/www nel container
```
Questo significa che puoi **modificare i file sul tuo computer** con il tuo editor preferito, e le modifiche sono **immediatamente visibili** nel browser (no rebuild necessario per PHP/Blade).
### Aggiungere una colonna al database
1. Crea la migration:
```bash
docker compose exec app php artisan make:migration add_website_to_customers
```
2. Modifica il file creato in `database/migrations/`
3. Esegui la migration:
```bash
docker compose exec app php artisan migrate
```
4. Aggiorna il Model (aggiungi il campo in `$fillable`)
5. Aggiorna le view per mostrare/editare il campo
### Aggiungere una nuova sezione (es. Contratti)
1. **Migration**: `php artisan make:migration create_contracts_table`
2. **Model**: `php artisan make:model Contract`
3. **Controller**: aggiungi i metodi CRUD
4. **Route**: aggiungi `Route::resource('contracts', ContractController::class)`
5. **Views**: crea `resources/views/contracts/`
6. **Sidebar**: aggiungi il link in `resources/views/layouts/app.blade.php`
---
## Prossimi passi
Suggerimenti per espandere questo progetto:
### Livello base
- [ ] Aggiungere l'autenticazione con `php artisan breeze:install`
- [ ] Aggiungere il campo "website" ai clienti
- [ ] Esportare i clienti in CSV
### Livello intermedio
- [ ] Aggiungere la gestione contratti (relazione 1-N con Customer)
- [ ] Upload logo aziendale (Laravel Storage + S3)
- [ ] Inviare email al cliente con Laravel Mail
- [ ] Test automatici con PestPHP
### Livello avanzato
- [ ] API REST con Laravel Sanctum
- [ ] Job asincroni per import massivo clienti
- [ ] Real-time con Laravel Broadcasting + Pusher
- [ ] Deploy in produzione su AWS/DigitalOcean
---
## Risoluzione problemi
### "Permissions denied" su storage/
```bash
docker compose exec app chmod -R 775 storage bootstrap/cache
```
### Il database non si connette
```bash
# Controlla che il container MySQL sia avviato e sano
docker compose ps
# Attendi che sia "healthy" (può impiegare 20-30 secondi al primo avvio)
docker compose logs db
```
### Svuota tutta la cache
```bash
docker compose exec app php artisan optimize:clear
```
### Riparti da zero (⚠️ cancella tutti i dati)
```bash
docker compose down -v
docker compose up -d --build
```