# Laravel 11: Creare un'applicazione completa da zero > Guida completa a Laravel 11: installazione, modelli Eloquent, autenticazione con Breeze, controller, rotte, autorizzazione con le policy, REST API con Sanctum, test con Pest e deploy in produzione. - Published: 2026-01-14 - Updated: 2026-04-08 - Author: SharpSkill - Tags: laravel, php, rest api, eloquent, tutorial - Reading time: 15 min --- Laravel 11 introduce una struttura di progetto semplificata, health check nativi e una configurazione rinnovata basata su `bootstrap/app.php`. Questa guida copre l'intero percorso: dall'installazione al deploy in produzione di un'applicazione di gestione task. > **Novità di Laravel 11** > > Laravel 11 elimina numerosi file boilerplate (Http Kernel, Console Kernel, directory Middleware) e adotta una configurazione centralizzata in `bootstrap/app.php`. Tra le novità: rotte health check, rotazione graduale delle chiavi di cifratura e un ecosistema rinnovato di starter kit. ## Installazione e configurazione del progetto Il Laravel installer genera lo scheletro del progetto in pochi secondi. Requisiti: PHP 8.2+ e Composer. ```bash # Install Laravel installer globally composer global require laravel/installer # Create a new Laravel 11 project laravel new taskmanager # Navigate to the project cd taskmanager ``` Il file `.env` configura la connessione al database. Laravel 11 utilizza SQLite come default; per ambienti di produzione, PostgreSQL o MySQL rappresentano la scelta consigliata. ```env # .env DB_CONNECTION=pgsql DB_HOST=127.0.0.1 DB_PORT=5432 DB_DATABASE=taskmanager DB_USERNAME=postgres DB_PASSWORD=secret ``` Dopo la configurazione, si eseguono le migrazioni iniziali e si avvia il server di sviluppo. ```bash # Run initial migrations php artisan migrate # Start the development server php artisan serve ``` L'applicazione risulta ora raggiungibile all'indirizzo `http://localhost:8000`. ## Creazione di modelli e migrazioni I modelli Eloquent costituiscono il nucleo di ogni applicazione Laravel. Il comando seguente crea contemporaneamente modello, migrazione, factory e seeder. ```bash # Create model with migration, factory, and seeder php artisan make:model Task -mfs ``` La migrazione definisce la struttura della tabella. Da notare: `softDeletes()` abilita la cancellazione logica senza perdita di dati. ```php // database/migrations/xxxx_create_tasks_table.php public function up(): void { Schema::create('tasks', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained()->cascadeOnDelete(); $table->string('title'); $table->text('description')->nullable(); $table->enum('status', ['pending', 'in_progress', 'completed'])->default('pending'); $table->enum('priority', ['low', 'medium', 'high'])->default('medium'); $table->date('due_date')->nullable(); $table->timestamps(); $table->softDeletes(); }); } ``` Il modello Eloquent dichiara i campi fillable, i cast e la relazione con lo User. ```php // app/Models/Task.php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\Relations\BelongsTo; class Task extends Model { use HasFactory, SoftDeletes; protected $fillable = [ 'title', 'description', 'status', 'priority', 'due_date', 'user_id', ]; protected $casts = [ 'due_date' => 'date', ]; public function user(): BelongsTo { return $this->belongsTo(User::class); } } ``` > **Soft Deletes nella pratica** > > I Soft Deletes contrassegnano i record come eliminati senza rimuoverli fisicamente dal database. Questa tecnica risulta particolarmente utile per audit trail e ripristino dei dati. Con `Task::withTrashed()` si possono interrogare anche i record cancellati. ## Configurazione dell'autenticazione con Breeze Laravel Breeze offre una soluzione di autenticazione leggera con login, registrazione, reset della password e verifica email. ```bash # Install Breeze composer require laravel/breeze --dev # Scaffold with Blade stack php artisan breeze:install blade # Install frontend dependencies and build npm install && npm run build # Run migrations for auth tables php artisan migrate ``` Il modello User viene esteso con la relazione `hasMany` verso i Task. ```php // app/Models/User.php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; class User extends Authenticatable { use HasFactory, Notifiable; protected $fillable = [ 'name', 'email', 'password', ]; protected $hidden = [ 'password', 'remember_token', ]; protected function casts(): array { return [ 'email_verified_at' => 'datetime', 'password' => 'hashed', ]; } public function tasks(): HasMany { return $this->hasMany(Task::class); } } ``` ## Creazione di controller e rotte Un controller resourceful implementa tutte le operazioni CRUD. Laravel 11 utilizza la dichiarazione semplificata delle rotte in `routes/web.php`. ```php // app/Http/Controllers/TaskController.php namespace App\Http\Controllers; use App\Models\Task; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\View\View; use Illuminate\Http\RedirectResponse; class TaskController extends Controller { public function index(): View { $tasks = Auth::user()->tasks() ->orderBy('due_date') ->paginate(10); return view('tasks.index', compact('tasks')); } public function create(): View { return view('tasks.create'); } public function store(Request $request): RedirectResponse { $validated = $request->validate([ 'title' => 'required|string|max:255', 'description' => 'nullable|string', 'status' => 'in:pending,in_progress,completed', 'priority' => 'in:low,medium,high', 'due_date' => 'nullable|date|after:today', ]); Auth::user()->tasks()->create($validated); return redirect()->route('tasks.index') ->with('success', 'Task created successfully.'); } public function edit(Task $task): View { $this->authorize('update', $task); return view('tasks.edit', compact('task')); } public function update(Request $request, Task $task): RedirectResponse { $this->authorize('update', $task); $validated = $request->validate([ 'title' => 'required|string|max:255', 'description' => 'nullable|string', 'status' => 'in:pending,in_progress,completed', 'priority' => 'in:low,medium,high', 'due_date' => 'nullable|date', ]); $task->update($validated); return redirect()->route('tasks.index') ->with('success', 'Task updated successfully.'); } public function destroy(Task $task): RedirectResponse { $this->authorize('delete', $task); $task->delete(); return redirect()->route('tasks.index') ->with('success', 'Task deleted successfully.'); } } ``` Le rotte vengono registrate come resource route, protette dal middleware `auth`. ```php // routes/web.php use App\Http\Controllers\TaskController; Route::middleware(['auth', 'verified'])->group(function () { Route::resource('tasks', TaskController::class) ->except(['show']); }); ``` ## Configurazione delle policy di autorizzazione Le policy incapsulano la logica di autorizzazione e garantiscono che gli utenti possano modificare solo i propri task. ```bash # Generate a policy for the Task model php artisan make:policy TaskPolicy --model=Task ``` La policy verifica che l'utente autenticato sia il proprietario del task. ```php // app/Policies/TaskPolicy.php namespace App\Policies; use App\Models\Task; use App\Models\User; class TaskPolicy { public function update(User $user, Task $task): bool { return $user->id === $task->user_id; } public function delete(User $user, Task $task): bool { return $user->id === $task->user_id; } } ``` > **Sicurezza delle Policy** > > Le policy devono essere invocate in ogni metodo del controller che modifica dati. Senza controllo di autorizzazione, un utente potrebbe manipolare l'URL per modificare o eliminare record altrui. Il metodo `$this->authorize()` lancia automaticamente un'eccezione 403 se il controllo fallisce. ## Costruire una REST API con Sanctum Laravel Sanctum fornisce un sistema di autenticazione leggero per SPA e app mobile. A partire da Laravel 11, la configurazione API si completa con un singolo comando. ```bash # Install API support (includes Sanctum) php artisan install:api ``` Il controller API restituisce risposte JSON tramite API Resource, che controllano la struttura dei dati esposti. ```php // app/Http/Controllers/Api/TaskController.php namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; use App\Http\Resources\TaskResource; use App\Models\Task; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\AnonymousResourceCollection; class TaskController extends Controller { public function index(Request $request): AnonymousResourceCollection { $tasks = $request->user()->tasks() ->orderBy('created_at', 'desc') ->paginate(15); return TaskResource::collection($tasks); } public function store(Request $request): TaskResource { $validated = $request->validate([ 'title' => 'required|string|max:255', 'description' => 'nullable|string', 'status' => 'in:pending,in_progress,completed', 'priority' => 'in:low,medium,high', 'due_date' => 'nullable|date|after:today', ]); $task = $request->user()->tasks()->create($validated); return new TaskResource($task); } public function update(Request $request, Task $task): TaskResource { $this->authorize('update', $task); $validated = $request->validate([ 'title' => 'sometimes|required|string|max:255', 'description' => 'nullable|string', 'status' => 'in:pending,in_progress,completed', 'priority' => 'in:low,medium,high', 'due_date' => 'nullable|date', ]); $task->update($validated); return new TaskResource($task); } public function destroy(Task $task) { $this->authorize('delete', $task); $task->delete(); return response()->json(['message' => 'Task deleted'], 200); } } ``` L'API Resource definisce quali campi vengono inclusi nella risposta JSON. ```php // app/Http/Resources/TaskResource.php namespace App\Http\Resources; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; class TaskResource extends JsonResource { public function toArray(Request $request): array { return [ 'id' => $this->id, 'title' => $this->title, 'description' => $this->description, 'status' => $this->status, 'priority' => $this->priority, 'due_date' => $this->due_date?->format('Y-m-d'), 'created_at' => $this->created_at->toISOString(), 'updated_at' => $this->updated_at->toISOString(), ]; } } ``` Le rotte API sono protette dal middleware `auth:sanctum` di Sanctum. ```php // routes/api.php use App\Http\Controllers\Api\TaskController; Route::middleware('auth:sanctum')->group(function () { Route::apiResource('tasks', TaskController::class); }); ``` ## Test automatizzati con Pest Pest offre una sintassi elegante per i test PHP. I test seguenti coprono le operazioni CRUD e l'autorizzazione. ```php // tests/Feature/TaskTest.php use App\Models\Task; use App\Models\User; beforeEach(function () { $this->user = User::factory()->create(); }); test('user can create a task', function () { $response = $this->actingAs($this->user)->post('/tasks', [ 'title' => 'Test Task', 'description' => 'Test description', 'priority' => 'high', ]); $response->assertRedirect('/tasks'); $this->assertDatabaseHas('tasks', [ 'title' => 'Test Task', 'user_id' => $this->user->id, ]); }); test('user can update own task', function () { $task = Task::factory()->create(['user_id' => $this->user->id]); $response = $this->actingAs($this->user)->put("/tasks/{$task->id}", [ 'title' => 'Updated Task', 'status' => 'completed', 'priority' => 'low', ]); $response->assertRedirect('/tasks'); expect($task->fresh()->title)->toBe('Updated Task'); }); test('user cannot update another users task', function () { $otherUser = User::factory()->create(); $task = Task::factory()->create(['user_id' => $otherUser->id]); $response = $this->actingAs($this->user)->put("/tasks/{$task->id}", [ 'title' => 'Hacked Task', ]); $response->assertForbidden(); }); test('user can delete own task', function () { $task = Task::factory()->create(['user_id' => $this->user->id]); $response = $this->actingAs($this->user)->delete("/tasks/{$task->id}"); $response->assertRedirect('/tasks'); $this->assertSoftDeleted('tasks', ['id' => $task->id]); }); test('api returns paginated tasks', function () { Task::factory(20)->create(['user_id' => $this->user->id]); $response = $this->actingAs($this->user)->getJson('/api/tasks'); $response->assertOk() ->assertJsonCount(15, 'data') ->assertJsonStructure([ 'data' => [['id', 'title', 'status', 'priority']], 'meta' => ['current_page', 'last_page'], ]); }); ``` L'esecuzione dei test avviene con un singolo comando. ```bash # Run all tests php artisan test # Run with coverage php artisan test --coverage ``` ## Ottimizzazioni per la produzione Prima del deploy, i seguenti comandi di cache migliorano significativamente le prestazioni. ```bash # Cache configuration, routes, and views php artisan config:cache php artisan route:cache php artisan view:cache # Optimize autoloader composer install --optimize-autoloader --no-dev ``` Il file `.env.production` contiene le impostazioni specifiche per la produzione. ```env # .env.production APP_ENV=production APP_DEBUG=false APP_URL=https://taskmanager.example.com LOG_CHANNEL=stack LOG_LEVEL=error CACHE_STORE=redis SESSION_DRIVER=redis QUEUE_CONNECTION=redis ``` > **Variabili d'ambiente sensibili** > > Il file `.env` non deve mai essere inserito nel version control. `APP_KEY`, le password del database e le chiavi API vanno configurate tramite variabili d'ambiente sicure del provider di hosting. Il comando `php artisan env:encrypt` cifra il file per deploy sicuri. ## Conclusione Questa guida ha trattato i componenti fondamentali di un'applicazione Laravel 11: - **Setup del progetto**: Laravel installer, configurazione dell'ambiente e migrazioni iniziali - **Modelli Eloquent**: relazioni, cast, soft deletes e factory per una gestione strutturata dei dati - **Autenticazione**: Breeze fornisce login, registrazione e reset password in pochi minuti - **Controller e rotte**: controller resourceful e gruppi di rotte protetti da middleware - **Autorizzazione**: le policy garantiscono che ogni utente gestisca solo le proprie risorse - **REST API**: token Sanctum e API Resource per client mobile e SPA - **Test**: i test Pest coprono le operazioni CRUD e le regole di autorizzazione - **Produzione**: caching, ottimizzazione dell'autoloader e configurazione sicura dell'ambiente --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/it/blog/laravel/laravel-11-building-complete-application