Prologo
Primi Passi
Architettura
Le Basi
- Routing
- Middleware
- Protezione da CSRF
- Controller
- Richieste
- Risposte
- Views
- Blade
- Vite
- URL
- Sessioni
- Validazione
- Errori
- Logging
Approfondimenti
- Artisan
- Broadcasting
- Cache
- Collezioni
- Concorrenza
- Contesto
- Contratti
- Eventi
- File System
- Helpers
- Client HTTP
- Localizzazione
- Notifiche
- Sviluppo di Package
- Processi
- Code
- Rate-limiting
- Stringhe
- Scheduling di Task
Sicurezza
Database
Eloquent ORM
Testing
Package
Controller
- Introduzione
- Scrivere i Controller
- Middleware del Controller
- Controller per una Risorsa
- Iniezione delle Dipendenze e Controller
Introduzione
Invece di definire tutta la logica per gestire le richieste come closure nei file delle rotte, potresti preferire organizzare questo comportamento utilizzando classi "controller". I controller possono raggruppare logiche di gestione delle richieste correlate in un’unica classe. Ad esempio, una classe UserController
potrebbe gestire tutte le richieste relative agli utenti, come mostrare, creare, aggiornare e cancellare utenti. Per impostazione predefinita, i controller sono memorizzati nella directory app/Http/Controllers
.
Scrivere i Controller
Controller di Base
Per creare rapidamente un nuovo controller, puoi eseguire il comando Artisan make:controller
. Di default, tutti i controller della tua applicazione sono memorizzati nella directory app/Http/Controllers
:
php artisan make:controller UserController
Diamo un’occhiata a un esempio di controller di base. Un controller può avere diversi metodi pubblici che rispondono alle richieste HTTP in arrivo:
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\View\View;
class UserController extends Controller
{
/**
* Mostra il profilo di un determinato utente.
*/
public function show(string $id): View
{
return view('user.profile', [
'user' => User::findOrFail($id)
]);
}
}
Una volta scritta una classe controller e un metodo, puoi definire una rotta verso il metodo del controller in questo modo:
use App\Http\Controllers\UserController;
Route::get('/user/{id}', [UserController::class, 'show']);
Quando una richiesta in arrivo corrisponde all’URI della rotta specificata, verrà invocato il metodo show
della classe App\Http\Controllers\UserController
e i parametri della rotta saranno passati al metodo.
I controller non sono obbligatoriamente estesi da una classe base. Tuttavia, a volte è comodo estendere una classe controller base che contiene metodi da condividere tra tutti i tuoi controller.
Single Action Controllers
Se un’azione del controller è particolarmente complessa, potresti trovare comodo dedicare un’intera classe controller a quella singola azione. Per fare ciò, puoi definire un unico metodo __invoke
all’interno del controller:
<?php
namespace App\Http\Controllers;
class ProvisionServer extends Controller
{
/**
* Provision a new web server.
*/
public function __invoke()
{
// ...
}
}
Quando registri le rotte per controller a singola azione, non è necessario specificare un metodo del controller. Invece, puoi semplicemente passare il nome del controller al router:
use App\Http\Controllers\ProvisionServer;
Route::post('/server', ProvisionServer::class);
Puoi generare un controller invocabile utilizzando l’opzione --invokable
del comando make:controller
di Artisan:
php artisan make:controller ProvisionServer --invokable
I template dei controller possono essere personalizzati utilizzando la pubblicazione degli stub.
Middleware del Controller
Puoi assegnare uno o più middleware alle rotte del controller nei tuoi file di route:
Route::get('/profile', [UserController::class, 'show'])->middleware('auth');
Oppure, potresti trovare comodo specificare il middleware all’interno della classe del tuo controller. Per farlo, il tuo controller dovrebbe implementare l’interfaccia HasMiddleware
, che richiede che il controller abbia un metodo statico middleware
. Da questo metodo, puoi restituire un array di middleware che dovrebbero essere applicati alle azioni del controller:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Routing\Controllers\HasMiddleware;
use Illuminate\Routing\Controllers\Middleware;
class UserController extends Controller implements HasMiddleware
{
/**
* Ottieni il middleware che dovrebbe essere assegnato al controller.
*/
public static function middleware(): array
{
return [
'auth',
new Middleware('log', only: ['index']),
new Middleware('subscribed', except: ['store']),
];
}
// ...
}
Puoi anche definire il middleware del controller come closure, che fornisce un modo comodo per definire un middleware inline senza scrivere un’intera classe middleware:
use Closure;
use Illuminate\Http\Request;
/**
* Ottieni il middleware che dovrebbe essere assegnato al controller.
*/
public static function middleware(): array
{
return [
function (Request $request, Closure $next) {
return $next($request);
},
];
}
Controller per una Risorsa
Se consideri ogni modello Eloquent nella tua applicazione come una "risorsa", è comune eseguire lo stesso set di azioni su ogni risorsa. Ad esempio, immagina che la tua applicazione contenga un modello Photo
e un modello Movie
. È probabile che gli utenti possano creare, leggere, aggiornare o eliminare queste risorse.
Per questo caso d’uso comune, il routing delle risorse di Laravel assegna le tipiche rotte di creazione, lettura, aggiornamento ed eliminazione ("CRUD") a un controller con una sola riga di codice. Per iniziare, possiamo usare l’opzione --resource
del comando Artisan make:controller
per creare rapidamente un controller che gestisca queste azioni:
php artisan make:controller PhotoController --resource
Questo comando genererà un controller in app/Http/Controllers/PhotoController.php
. Il controller conterrà un metodo per ciascuna delle operazioni di risorsa disponibili. Successivamente, puoi registrare una rotta di risorsa che punta al controller:
use App\Http\Controllers\PhotoController;
Route::resource('photos', PhotoController::class);
Questa singola dichiarazione di rotta crea più rotte per gestire diverse azioni sulla risorsa. Il controller generato avrà già i metodi scheletro per ciascuna di queste azioni. Ricorda, puoi sempre ottenere una rapida panoramica delle rotte della tua applicazione eseguendo il comando Artisan route:list
.
Puoi anche registrare molti controller di risorsa contemporaneamente passando un array al metodo resources
:
Route::resources([
'photos' => PhotoController::class,
'posts' => PostController::class,
]);
Azioni gestite dai controller delle risorse
Verbo | URI | Azione | Nome Rotta |
---|---|---|---|
GET | /photos |
index | photos.index |
GET | /photos/create |
create | photos.create |
POST | /photos |
store | photos.store |
GET | /photos/{photo} |
show | photos.show |
GET | /photos/{photo}/edit |
edit | photos.edit |
PUT/PATCH | /photos/{photo} |
update | photos.update |
DELETE | /photos/{photo} |
destroy | photos.destroy |
Personalizzare il Comportamento per un Model Mancante
Normalmente, viene generata una risposta HTTP 404 se non viene trovato un modello di risorsa con binding implicito. Tuttavia, puoi personalizzare questo comportamento chiamando il metodo missing
quando definisci una rotta per risorsa. Il metodo missing
accetta una closure che verrà eseguita se non si trova un modello legato implicitamente per una qualsiasi delle rotte della risorsa:
use App\Http\Controllers\PhotoController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redirect;
Route::resource('photos', PhotoController::class)
->missing(function (Request $request) {
return Redirect::route('photos.index');
});
Modelli Soft Deleted
Normalmente, il binding implicito dei modelli non recupera i modelli che sono stati soft deleted e restituirà invece una risposta HTTP 404. Tuttavia, puoi indicare al framework di permettere i modelli soft deleted utilizzando il metodo withTrashed
quando definisci la route della risorsa:
use App\Http\Controllers\PhotoController;
Route::resource('photos', PhotoController::class)->withTrashed();
Chiamare withTrashed
senza argomenti permetterà i modelli soft deleted per le route della risorsa show
, edit
e update
. Puoi specificare un sottoinsieme di queste route passando un array al metodo withTrashed
:
Route::resource('photos', PhotoController::class)->withTrashed(['show']);
Specificare il Modello della Risorsa
Se stai usando il route model binding e desideri che i metodi del controller delle risorse indichino un’istanza del modello, puoi usare l’opzione --model
quando generi il controller:
php artisan make:controller PhotoController --model=Photo --resource
Generazione delle Form Request
Puoi usare l’opzione --requests
quando crei un controller di tipo resource per far sì che Artisan generi le classi di form request per i metodi di storage e update del controller:
php artisan make:controller PhotoController --model=Photo --resource --requests
Rotte di Risorse Parziali
Quando si dichiara una rotta di risorsa, è possibile specificare un sottoinsieme di azioni che il controller dovrebbe gestire invece dell’intero insieme delle azioni predefinite:
use App\Http\Controllers\PhotoController;
Route::resource('photos', PhotoController::class)->only([
'index', 'show'
]);
Route::resource('photos', PhotoController::class)->except([
'create', 'store', 'update', 'destroy'
]);
Rotte per Risorse – API
Quando dichiari rotte risorsa destinate ad essere consumate da API, solitamente vorrai escludere rotte che presentano template HTML come create
ed edit
. Per comodità, puoi usare il metodo apiResource
per escludere automaticamente queste due rotte:
use App\Http\Controllers\PhotoController;
Route::apiResource('photos', PhotoController::class);
Puoi registrare molti controller risorsa API contemporaneamente passando un array al metodo apiResources
:
use App\Http\Controllers\PhotoController;
use App\Http\Controllers\PostController;
Route::apiResources([
'photos' => PhotoController::class,
'posts' => PostController::class,
]);
Per generare rapidamente un controller risorsa API che non includa i metodi create
o edit
, usa l’opzione --api
quando esegui il comando make:controller
:
php artisan make:controller PhotoController --api
Risorse Annidate
A volte potresti aver bisogno di definire delle rotte per una risorsa annidata. Per esempio, una risorsa foto può avere più commenti associati. Per annidare i controller delle risorse, puoi usare la notazione a "punto" nella dichiarazione della rotta:
use App\Http\Controllers\PhotoCommentController;
Route::resource('photos.comments', PhotoCommentController::class);
Questa rotta registrerà una risorsa annidata che potrà essere accessibile tramite URI come le seguenti:
/photos/{photo}/comments/{comment}
Limitazione delle Risorse Annidate
La funzionalità di implicit model binding di Laravel può automaticamente limitare i binding annidati in modo che il modello figlio risolto appartenga al modello padre. Utilizzando il metodo scoped
quando definisci la tua risorsa annidata, puoi abilitare la limitazione automatica e indicare a Laravel quale campo utilizzare per recuperare la risorsa figlio.
Annidamento Superficiale
Spesso non è del tutto necessario avere sia gli ID del genitore che quelli del figlio all’interno di un URI, poiché l’ID del figlio è già un identificatore univoco. Quando si utilizzano identificatori univoci come chiavi primarie auto-incrementanti per identificare i tuoi modelli nei segmenti URI, puoi scegliere di usare l’"annidamento superficiale":
use App\Http\Controllers\CommentController;
Route::resource('photos.comments', CommentController::class)->shallow();
Questa definizione di route definirà le seguenti rotte:
Verb | URI | Action | Route Name |
---|---|---|---|
GET | /photos/{photo}/comments |
index | photos.comments.index |
GET | /photos/{photo}/comments/create |
create | photos.comments.create |
POST | /photos/{photo}/comments |
store | photos.comments.store |
GET | /comments/{comment} |
show | comments.show |
GET | /comments/{comment}/edit |
edit | comments.edit |
PUT/PATCH | /comments/{comment} |
update | comments.update |
DELETE | /comments/{comment} |
destroy | comments.destroy |
Assegnare Nomi alle Resource Route
Per impostazione predefinita, tutte le azioni del controller delle risorse hanno un nome di route; tuttavia, puoi sovrascrivere questi nomi passando un array names
con i nomi di route desiderati:
use App\Http\Controllers\PhotoController;
Route::resource('photos', PhotoController::class)->names([
'create' => 'photos.build'
]);
Nominare i Parametri delle Route delle Risorse
Per impostazione predefinita, Route::resource
creerà i parametri delle route per le tue risorse basandosi sulla versione "singolare" del nome della risorsa. Puoi facilmente sovrascriverlo per ogni singola risorsa usando il metodo parameters
. L’array passato al metodo parameters
dovrebbe essere un array associativo di nomi di risorse e nomi dei parametri:
use App\Http\Controllers\AdminUserController;
Route::resource('users', AdminUserController::class)->parameters([
'users' => 'admin_user'
]);
L’esempio sopra genera il seguente URI per la route show
della risorsa:
/users/{admin_user}
Rotte delle Risorse con Scoping
La funzionalità di scoped implicit model binding di Laravel può automaticamente delimitare i binding annidati, assicurando che il modello figlio risolto appartenga al modello genitore. Usando il metodo scoped
quando definisci la tua risorsa annidata, puoi abilitare lo scoping automatico e indicare a Laravel quale campo deve essere usato per recuperare la risorsa figlio:
use App\Http\Controllers\PhotoCommentController;
Route::resource('photos.comments', PhotoCommentController::class)->scoped([
'comment' => 'slug',
]);
Questa rotta registrerà una risorsa annidata con scoping che può essere accessibile con URI come i seguenti:
/photos/{photo}/comments/{comment:slug}
Quando utilizzi un binding implicito personalizzato come parametro di rotta annidato, Laravel delimita automaticamente la query per recuperare il modello annidato dal genitore, seguendo le convenzioni per indovinare il nome della relazione sul genitore. In questo caso, si presumerà che il modello Photo
abbia una relazione chiamata comments
(il plurale del nome del parametro di rotta) che può essere usata per recuperare il modello Comment
.
Localizzazione degli URI delle risorse
Per impostazione predefinita, Route::resource
creerà URI delle risorse utilizzando verbi e regole di plurale in inglese. Se hai bisogno di localizzare i verbi delle azioni create
ed edit
, puoi usare il metodo Route::resourceVerbs
. Questo può essere fatto all’inizio del metodo boot
all’interno di App\Providers\AppServiceProvider
della tua applicazione:
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Route::resourceVerbs([
'create' => 'crea',
'edit' => 'modifica',
]);
}
Il pluralizzatore di Laravel supporta diverse lingue che puoi configurare in base alle tue esigenze. Una volta personalizzati i verbi e la lingua per la pluralizzazione, una registrazione della rotta risorsa come Route::resource('pubblicazione', PublicacionController::class)
produrrà i seguenti URI:
/pubblicazione/crea
/pubblicazione/{pubblicazione}/modifica
Aggiungere Rotte ai Resource Controllers
Se hai bisogno di aggiungere rotte extra a un resource controller oltre al set predefinito di rotte, definisci queste rotte prima di chiamare il metodo Route::resource
. Altrimenti, le rotte definite dal metodo resource
potrebbero avere la precedenza sulle tue rotte aggiuntive:
use App\Http\Controller\PhotoController;
Route::get('/photos/popular', [PhotoController::class, 'popular']);
Route::resource('photos', PhotoController::class);
Ricorda di mantenere i tuoi controller focalizzati su un solo aspetto specifico della tua applicazione. Se ti accorgi di aver bisogno frequentemente di metodi al di fuori delle azioni standard della risorsa, considera di dividere il tuo controller in due controller più piccoli.
Controller di Risorse – Singleton
A volte, la tua applicazione avrà risorse che possono avere solo un’unica istanza. Ad esempio, il "profilo" di un utente può essere modificato o aggiornato, ma un utente potrebbe non avere più di un "profilo". Allo stesso modo, un’immagine può avere un singolo "thumbnail". Queste risorse sono chiamate "singleton resources", il che significa che può esistere una sola istanza della risorsa. In questi casi, puoi registrare un controller di risorse "singleton":
use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;
Route::singleton('profile', ProfileController::class);
La definizione di risorsa singleton sopra registrerà le rotte qui di seguito. Come puoi vedere, le rotte di "creazione" non sono registrate per le risorse singleton, e le rotte registrate non accettano un identificatore poiché può esistere solo un’istanza della risorsa:
Metodo | URI | Azione | Nome Rotta |
---|---|---|---|
GET | /profile |
show | profile.show |
GET | /profile/edit |
edit | profile.edit |
PUT/PATCH | /profile |
update | profile.update |
Le risorse singleton possono anche essere annidate all’interno di una risorsa standard:
Route::singleton('photos.thumbnail', ThumbnailController::class);
In questo esempio, la risorsa photos
conterebbe tutte le rotte standard per una risorsa; tuttavia, la risorsa thumbnail
sarebbe una risorsa singleton con le seguenti rotte:
Metodo | URI | Azione | Nome Rotta |
---|---|---|---|
GET | /photos/{photo}/thumbnail |
show | photos.thumbnail.show |
GET | /photos/{photo}/thumbnail/edit |
edit | photos.thumbnail.edit |
PUT/PATCH | /photos/{photo}/thumbnail |
update | photos.thumbnail.update |
Risorse Singleton – Creazione
A volte, potresti voler definire le rotte di creazione e memorizzazione per una risorsa singleton. Per farlo, puoi utilizzare il metodo creatable
quando registri la rotta della risorsa singleton:
Route::singleton('photos.thumbnail', ThumbnailController::class)->creatable();
In questo esempio, verranno registrate le seguenti rotte. Come puoi vedere, una rotta DELETE
sarà registrata anche per le risorse singleton creabili:
Verbo | URI | Azione | Nome Rotta |
---|---|---|---|
GET | /photos/{photo}/thumbnail/create |
create | photos.thumbnail.create |
POST | /photos/{photo}/thumbnail |
store | photos.thumbnail.store |
GET | /photos/{photo}/thumbnail |
show | photos.thumbnail.show |
GET | /photos/{photo}/thumbnail/edit |
edit | photos.thumbnail.edit |
PUT/PATCH | /photos/{photo}/thumbnail |
update | photos.thumbnail.update |
DELETE | /photos/{photo}/thumbnail |
destroy | photos.thumbnail.destroy |
Se vuoi che Laravel registri la rotta DELETE
per una risorsa singleton ma non registri le rotte di creazione o memorizzazione, puoi utilizzare il metodo destroyable
:
Route::singleton(...)->destroyable();
Risorse Singleton – API
Il metodo apiSingleton
può essere utilizzato per registrare una risorsa singleton che verrà gestita tramite un’API, rendendo quindi inutili le rotte create
ed edit
:
Route::apiSingleton('profile', ProfileController::class);
Naturalmente, le risorse API singleton possono anche essere creatable
, il che registrerà le rotte store
e destroy
per la risorsa:
Route::apiSingleton('photos.thumbnail', ProfileController::class)->creatable();
Iniezione delle Dipendenze e Controller
Iniezione nel Costruttore
Il service container di Laravel viene utilizzato per risolvere tutti i controller di Laravel. Di conseguenza, puoi usare il type-hint per qualsiasi dipendenza il tuo controller possa necessitare nel suo costruttore. Le dipendenze dichiarate saranno automaticamente risolte e iniettate nell’istanza del controller:
<?php
namespace App\Http\Controllers;
use App\Repositories\UserRepository;
class UserController extends Controller
{
/**
* Crea una nuova istanza del controller.
*/
public function __construct(
protected UserRepository $users,
) {}
}
Injection dei Metodi
Oltre all’injection nel costruttore, puoi anche specificare il tipo delle dipendenze nei metodi del tuo controller. Un caso d’uso comune per l’injection dei metodi è iniettare l’istanza Illuminate\Http\Request
nei metodi del tuo controller:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class UserController extends Controller
{
/**
* Salva un nuovo utente.
*/
public function store(Request $request): RedirectResponse
{
$name = $request->name;
// Salva l'utente...
return redirect('/users');
}
}
Se il metodo del tuo controller si aspetta anche input da un parametro di una route, elenca gli argomenti della route dopo le altre dipendenze. Per esempio, se la tua route è definita così:
use App\Http\Controllers\UserController;
Route::put('/user/{id}', [UserController::class, 'update']);
Puoi comunque specificare il tipo di Illuminate\Http\Request
e accedere al parametro id
definendo il metodo del tuo controller come segue:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class UserController extends Controller
{
/**
* Aggiorna l'utente specificato.
*/
public function update(Request $request, string $id): RedirectResponse
{
// Aggiorna l'utente...
return redirect('/users');
}
}