2026-03-30 19:47:43 +02:00
2026-03-30 19:15:13 +02:00
2026-03-30 19:15:13 +02:00
2026-03-30 19:15:13 +02:00
2026-03-30 19:15:13 +02:00
2026-03-30 19:15:13 +02:00
2026-03-30 19:15:13 +02:00
2026-03-30 19:15:13 +02:00
2026-03-30 19:15:13 +02:00
2026-03-30 19:15:13 +02:00
2026-03-30 19:47:43 +02:00
2026-03-30 19:15:13 +02:00
2026-03-30 19:15:13 +02:00
2026-03-30 19:15:13 +02:00
2026-03-30 19:15:13 +02:00
2026-03-30 19:38:34 +02:00
2026-03-30 19:15:13 +02:00

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
  2. Pre-requisiti
  3. Avvio rapido
  4. Struttura del progetto
  5. Architettura Docker
  6. Concetti Laravel spiegati
  7. Configurazione: .env vs Impostazioni dinamiche
  8. CRUD Clienti — come funziona
  9. Comandi utili
  10. Workflow di sviluppo
  11. 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

# 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)

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:

// 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:

// 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:

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:

{{-- 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:

$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'):

<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:

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):

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:

// 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

// 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:

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:

// 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

# 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)

# 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:

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:

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:
    docker compose exec app php artisan make:migration add_website_to_customers
    
  2. Modifica il file creato in database/migrations/
  3. Esegui la migration:
    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/

docker compose exec app chmod -R 775 storage bootstrap/cache

Il database non si connette

# 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

docker compose exec app php artisan optimize:clear

Riparti da zero (⚠️ cancella tutti i dati)

docker compose down -v
docker compose up -d --build
Description
No description provided
Readme 78 KiB
Languages
PHP 47.1%
Blade 43.7%
Shell 5.2%
Dockerfile 4%