Introduzione
I controller, come già lievemente introdotto nelle guide precedenti, servono a gestire gli input da parte dell'utente e a organizzare la logica della gestione delle richieste in una classe specifica.
Seguendo la serie dei tutorial su Laravel, riprenderemo l'esempio di testdb con riferimento alla tabella Students.
Costruire un controller
Controller Base
Un controller, a livello basilare, è una estensione della classe Controller, già inclusa nella configurazione di default di Laravel.
La classe è reperibile attraverso il percorso App\Http\Controllers\Controller.
Una prima sintassi di come apparirebbe un controller, sarebbe la seguente:
namespace App\Http\Controllers;
use App\Models\Student;
class StudentController extends Controller
{
/**
* Show the info of a selected Student.
*
* @param int $badge_number
* @return \Illuminate\View\View
*/
public function show($badge_number)
{
return view('student.profile', [
'student' => Student::findOrFail($badge_number)
]);
}
}
Ora occorre definire un percorso verso il metodo del tuo controller. Per esempio, per creare un collegamento all'operatore show, che prenderà in input i parametri della classe e li visualizzerà a video.
use App\Http\Controllers\StudentController;
Route::get('/student/{badge_number}', [StudentController::class, 'show']);
Controllers Single Action
Può accadere che alcuni metodi siano particolarmente sofisticati e necessitino di controllers ad hoc dove dettagliare unicamente l'azione per cui essi sono preposti.
Come nell'esempio, occorrerà definire un metodo __invoke all'interno del Controller
namespace App\Http\Controllers;
use App\Models\Student;
class ProvisionServer extends Controller
{
/**
* Provision a new web server.
*
* @return \Illuminate\Http\Response
*/
public function __invoke()
{
// Body of your function
}
}
Come prima, occorrerà ora che tu crei un percorso col quale richiamare il metodo del controller:
use App\Http\Controllers\ProvisionServer;
Route::post('/server', ProvisionServer::class);
Un metodo alternativo per generare rapidamente un controller con opzione --invokable è quello di utilizzare php artisan:
$ php artisan make:controller ProvisionServer --invokable
Middleware per i controller
I Middlewares sono degli "strati" che si interpongono tra una richiesta HTTP e la tua applicazione. La loro utilità è quella di mettere a disposizione ulteriori metodi come, ad esempio, un metodo per controllare che un utente sia autenticato prima di interagire con le funzionalità della tua applicazione.
Una grande varietà di middleware è disponibile per Laravel ed essi possono essere assegnati a un controller, integrandoli nel percorso. Per esempio:
Route::get('profile', [StudentController::class, 'show'])->middleware('auth');
I middlewares possono anche essere definiti nel costruttore del tuo controller, invece che nel percorso. Nell'esempio, il middleware controllerà se l'utente sia loggato ed esista nel sistema, prima di permettere altri metodi del controller o reindirizzare l'utente alla schermata di login:
class StudentController extends Controller
{
/**
* Instantiate a new instance
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
$this->middleware('log')->only('index');
$this->middleware('subscribed')->except('store');
}
}
Controllers di risorse
Come abbiamo visto nella guida sulla creazione di modelli, la componente Eloquent contribuirà, per ogni "risorsa" del Modello, alla assegnazione di un controller ad hoc che gestisca le operazioni CRUD(create, read, update, delete). Tali operazioni sono fondamentali per gestire le risorse o oggetti del Model : il vantaggio è che puoi creare questi controller con una sola linea di codice!
Immaginando di voler creare un controller per la risorsa Student, ti sarà sufficiente il comando di Artisan, associando l'opzione --resource per indicare appunto che, quello creato, sarà un controller per risorsa:
$ php artisan make:controller StudentController --resource
Il nuovo controller creato sarà disponibile nel percorso app/Http/Controllers/StudentController.php ed esso avrà a disposizione le operazioni CRUD. Il prossimo passo sarà impostare il percorso che metterà in relazione il controller e la corrispondente risorsa.
La sintassi sarà simile all'esempio:
use App\Http\Controllers\StudentController;
Route::resource('students', StudentController::class);
Verranno, quindi, creati una serie di percorsi per le operazioni da poter eseguire sulla risorsa indicata. Ti sarà possibile visionare tutta la lista dei percorsi definita nella tua applicazione web tramite il comando di Artisan:
$ php artisan route:list
E' possibile, infine, utilizzare gli array per passare più argomenti alla dichiarazione Route, così da poter inserire al suo interno tutti i collegamenti tra Risorse e Controller che preferisci. La sintassi sarà simile:
Route::resources([
'students' => StudentController::class,
'teachers' => TeacherController::class,
]);
Specificare il modello associato alla risorsa
Nel caso in cui volessi essere tu stesso a stabilire con esattezza a quale modello faccia riferimento la risorsa e, di conseguenza, il relativo controller definito, potrai usare l'opzione di Artisan chiamata --model. La sintassi sarà simile all'esempio:
$ php artisan make:controller StudentController --model=Student --resource
Creare richieste con form
E' possibile anche creare delle classi di richieste con form per controllare i metodi di salvataggio e di aggiornamento delle risorse:
$ php artisan make:controller StudentController --model=Student --resource --requests
Le classi descritte, di solito, servono ad incapsulare una logica di validazione o autorizzazione legate alla compilazione dei form. Puoi approfondire l'argomento nella Documentazione di Laravel - Form Request Validation.
Permettere solo un sottoinsieme di operazioni
Nel caso in cui tu non volessi assegnare tutte le operazioni CRUD a una risorsa, potrai specificare, come nell'esempio, quelle da te selezionate per essa:
use App\Http\Controllers\StudentController;
Route::resource('students', StudentController::class)->only([
'index', 'show'
]);
Route::resource('students', StudentController::class)->except([
'create', 'store', 'update', 'destroy'
]);
Come puoi osservare, potrai usare la semantica di only o di except per l'assegnazione degli operatori.
Risorse destinate alle API
Nel caso di risorse destinate solo alla fruizione da parte delle API, potresti aver bisogno di escludere alcuni metodi come create e edit. Tramite la dichiarazione apiResource, potrai escludere i due operatori obsoleti appena citati.
La sintassi sarà come nell'esempio:
use App\Http\Controllers\StudentController;
Route::apiResource('students', StudentController::class);
Come precedentemente mostrato, anche in questo caso sarà possibile passare un array di risorse con lo stesso metodo di prima.
Infine, puoi generare direttamente un controller collegato a una risorsa API, tramite l'opzione --api di Artisan:
php artisan make:controller StudentController --api
Risorse innestate
Può capitare, nella progettazione dell'organizzazione dei dati della tua applicazione, che tu possa avere necessità di mettere in relazione più entità appartenenti a tabelle diverse.
Nel nostro esempio, un database per immagazzinare le informazioni sugli studenti di una scuola, un'ipotetica relazione sarebbe quella tra gli studenti e i loro voti. In Laravel si può permettere a un controller di controllare anche le "sottorisorse" collegate alla entità principale.
La sintassi è simile a quella dei casi precedenti ma si utilizza la notazione puntata seguita dalla risorsa che vorrai "nidificare" sotto la principale. Nell'esempio, volendo avere un controller che si occupi dei voti degli studenti:
use App\Http\Controllers\StudentGradeController;
Route::resource('student.grades', StudentGradeController::class);
La dichiarazione creerà, in fase di definizione del percorso, un URI (Uniform resource identifier) per identificare la risorsa nidificata collegata al controller.
La sintassi dell'URI sarà simile alla seguente:
/students/{student}/grades/{grade}
Rinominare
Assegnare dei nomi ai percorsi di risorsa
I nomi di default delle azioni disponibili per i controlli sulle risorse possono essere modificati passando un array e indicandolo con la voce names. Grazie a questo metodo, potrai cambiare i nomi delle funzioni disponibili per il controller.
L'uso di names si rifà a questa sintassi di esempio:
use App\Http\Controllers\StudentController;
Route::resource('students', StudentController::class)->names([
'create' => 'student.new'
]);
In questo modo, il "costruttore" di una entità studente potrà essere richiamato tramite la funzione student.new.
Assegnare dei nomi dai parametri nel percorso di risorsa
Durante la creazione di una risorsa, i nomi delle tabelle del Database vengono utilizzati anche per definire i nomi dei parametri della risorsa. E' possibile, ovviamente, scegliere invece i nomi da voler assegnare e passarli nel percorso tramite la voce parameters.
La sintassi seguirà l'esempio:
use App\Http\Controllers\SupportTeacherController;
Route::resource('teachers', SupportTeacherController::class)->parameters([
'teachers' => 'support_teacher'
]);
Usare lo Scoping per localizzare risorse specifiche nel percorso
Grazie allo Scoping, in Laravel è possibile identificare unicamente una risorsa tramite un identificatore univoco. Ciò permette quindi di inserire, all'interno dei percorsi, un riferimento alla singola risorsa identificata da una chiave, che di solito sarà lo slug (una parte o l'intera stringa del tuo URI). Lo slug viene appositamente usato come identificatore univoco più comprensibile, avendo una sintassi più simile al linguaggio naturale. Usando questa chiave, creerai una risorsa unica e un relativo percorso per accedervi. Questo tema viene approfondito nel successivo tutorial, dedicato al routing.
Tornando al metodo dello scoping, ti mostriamo quindi, all'interno del percorso, come suggerire a Laravel il campo in base a cui identificare univocamente la risorsa.
Per esempio, immagina di avere una serie di voti associati a uno studente ma di disporre di un identificatore univoco (lo slug) nell'applicazione per ricavare i dati di una singola interrogazione. All'interno del percorso, potrai definire il riferimento a una risorsa in base al suo slug, così da poter identificare tutti i singoli voti, in caso ci siano similitudini negli altri campi ad essi associati.
use App\Http\Controllers\StudentGradeController;
Route::resource('students.grades', StudentGradeController::class)->scoped([
'vote' => 'slug',
]);
L ' URI di risorsa generato sarà il seguente:
/students/{student}/grades/{grade:slug}
Aggiungere ulteriori percorsi ai Controllers
Ti è possibile associare percorsi addizionali al controller di una risorsa, a parte il set di default disponibile nella definizione base di un percorso. Nel caso in cui tu volessi far ciò, ricorda però di dichiarare i percorsi extra prima della dichiarazione resource poiché, se venissero dichiarati sotto tale stringa, si creerebbe una precedenza sul percorso di resource piuttosto che su quelli supplementari.
Nell'esempio:
use App\Http\Controller\StudentController;
Route::get('/students/1stclass', [StudentController::class, '1stclass']);
Route::resource('students', StudentController::class);
In questo modo avrai un percorso specifico per gli studenti di prima classe.
Injection di un Controller
Injection del costruttore
Laravel può risolvere le dipendenze specificate nel costruttore di un controller. Ciò permette di indicare nel costruttore di un Controller tutte le dipendenze che, una volta risolte da Laravel, comporranno l'istanza appena creata.
Una sintassi di esempio:
namespace App\Http\Controllers;
use App\Repositories\StudentRepository;
class StudentController extends Controller
{
/**
* The user repository instance.
*/
protected $students;
/**
* Create a new controller instance.
*
* @param \App\Repositories\StudentRepository $students
* @return void
*/
public function __construct(StudentRepository $students)
{
$this->students = $students;
}
}
Injection di altri metodi
Oltre al costruttore, potrai anche "innestare" altri metodi per il tuo controller, specificandone le opportune istruzioni e dipendenze.
Ad esempio, per integrare una Request nei metodi del controller sui voti dovrebbe permettere la gestione e la validazione dei dati digitati, quando si inserisce un nuovo voto per uno studente.
La sintassi di codice sarà la seguente:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class GradeController extends Controller
{
/**
* Store a new grade.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$name = $request->name;
//
}
}
Nel caso in cui tu necessitassi, come input del metodo definito, degli argomenti dal percorso che collega controller e risorsa, dovrai scrivere il parametro interessato negli input della funzione.
Nell'esempio dei voti, identificati dallo slug, se la definizione del percorso fosse la seguente:
use App\Http\Controllers\GradeController;
Route::put('/user/{slug}', [GradeController::class, 'update']);
Il codice con il metodo dichiarato nell'injection sarebbe così descritto:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class GradeController extends Controller
{
/**
* Update the given grade.
*
* @param \Illuminate\Http\Request $request
* @param string $slug
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $slug)
{
//
}
}
Conclusioni
Terminato il tutorial, dovresti aver preso confidenza con la definizione di Controller e il modo in cui essi operano con le risorse del Model. Avrai inoltre iniziato a capire come funziona la definizione dei percorsi che associano i controller alle risorse e come personalizzarne le opzioni in base alle tue necessità.
Nella prossima puntata di questa serie, scoprirai invece come creare i template e le viste per la tua applicazione web sviluppata con Laravel utilizzando il sistema di template Blade