Usare le Migrations di Laravel per versionare lo schema del database
Le migrazioni di Laravel consentono di versionare lo schema del database dell’applicazione e di legarlo in modo indissolubile al resto del software. Detto in maniera più semplice, le migrations sono delle classi di Laravel che, attraverso la Facade Schema, ci permettono di creare e modificare lo schema del database, in qualsiasi ambiente andremo ad installare l’applicazione.
In questo capitolo impareremo ad usarle. Cominciamo a localizzarle, aprendo la cartella database/migrations
Come possiamo vedere, in questa cartella ci sono alcuni files con estensione PHP. I files hanno un prefisso che indica la data in cui sono stati creati, seguito da un nome che ci dice l’azione della migration. Le 4 migrations che vediamo sono incluse di default in ogni progetto.
Ogni migration ha due metodi: up() e down(). Il primo viene eseguito per aggiungere una modifica allo schema, il secondo invece ha il compito di riportare lo schema alla situazione precedente.
In pratica, i metodi up() delle migrations vengono invocati quando si lancia questo comando
php artisan migrate
Al contrario, i metodi down() delle migrations vengono invocati quando si lancia questo comando
php artisan migrate:rollback
Detto questo, prima di buttarci a capofitto nella scrittura delle migrations facciamo quello che dovrebbe fare ogni buon programmatore: progettare il database.
Il nostro obiettivo è versionare questo database composto da cinque entità: Produttore, Modello, Macchina, Utente e Patente. La tabella car_user ci servirà per definire la relazione molti a molti tra macchine e utenti, considerando che un utente può possedere più macchine e la stessa macchina può essere cointestata a più persone. Inoltre dobbiamo conservare uno storico per risalire ad ogni passaggio di proprietà
Cominciamo modificando la migrations degli utenti, eliminando il campo name e aggiungendo i campi stringa first_name e last_name. Il metodo up di questa migration si dovrà presentare così
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('first_name');
$table->string('last_name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
Come vedete, stiamo usando il metodo create() della Facade Schema. Il primo parametro è il nome della tabella, mentre il secondo è una closure che gestisce la creazione dei campi. L’istruzione $table->timestamps() andrà a creare due campi di tipo data, created_at e updated_at, che Laravel utilizza per tenere traccia della creazione e dell’aggiornamento di ogni singolo record.
Adesso creiamo la migration dei produttori, come sempre utilizzando artisan
php artisan make:migration create_producers_table
Apriamo il file appena creato e implementiamo il metodo up() per aggiungere i campi necessari alla tabella.
public function up()
{
Schema::create('producers', callback: function (Blueprint $table) {
$table->id();
$table->string('name', 255);
$table->timestamps();
});
}
Ripetiamo la stessa operazione per creare la migration della tabella car_models, ma in questo caso nel metodo up dobbiamo impostare anche la relazione con la tabella producers
php artisan make:migration create_car_models_table
public function up()
{
Schema::create('car_models', callback: function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('producer_id');
$table->foreign('producer_id')->references('id')->on('producers');
$table->string('name', 255);
$table->decimal('price', 9, 3);
$table->timestamps();
});
}
Adesso creiamo la migration della tabella driving_licenses, dove impostiamo la relazione con la tabella users
php artisan make:migration create_driving_licenses_table
public function up()
{
Schema::create('driving_licenses', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->string('number', 255);
$table->timestamp('valided_until_at');
$table->timestamps();
});
}
Ora occupiamoci della migration per la tabella cars, dove ci sarà una relazione con la tabella car_models. E qui dobbiamo fare un appunto: le migrations devono girare in ordine sequenziale, a partire dal timestamp impostato come prefisso del file. Quindi dobbiamo fare attenzione quando impostiamo le relazioni, perché quando Laravel deve creare un vincolo relazionale, se non trova la tabella da relazionare, va in errore.
Detto questo, possiamo generare la migration
php artisan make:migration create_cars_table
Apriamo il nuovo file e inseriamo i campi come segue
public function up()
{
Schema::create('cars', callback: function (Blueprint $table) {
$table->id();
$table->string('license_plate', 15);
$table->unsignedBigInteger('car_model_id');
$table->foreign('car_model_id')->references('id')->on('car_models')->onDelete('cascade');
$table->timestamp('registered_at')->nullable();
$table->timestamps();
});
}
L’ultimo step è la creazione della pivot che ci serve per gestire la relazione molti a molti tra utenti e macchine. Qui utilizzeremo un ulteriore timestamp, per indicare la fine della decorrenza.
La pivot deve chiamarsi come la combinazione dei nomi delle due tabelle che la compongono (in questo caso al singolare), separate da un underscore.
php artisan make:migration create_car_user_table
E di seguito il relativo metodo up
public function up()
{
Schema::create('car_user', function (Blueprint $table) {
$table->unsignedBigInteger('car_id');
$table->foreign('car_id')->references('id')->on('cars')->onDelete('cascade');
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('id')->on('users');
$table->timestamp('ended_at')->nullable();
$table->timestamps();
});
}
Ora da terminale lanciamo il comando per lanciare le migration
php artisan migrate
Perfetto, il nostro database è stato creato. Ma prima di passare oltre, vediamo cosa succede se creiamo una nuova migration e successivamente lanciamo il comando per migrare lo schema.
php artisan make:migration create_tests_table
php artisan migrate
Come possiamo vedere, Laravel ha lanciato solo l’ultima migration, in quanto le altre erano già state lanciate. Questo è possibile perché all’interno del nostro database c’è una tabella chiamata migrations, dove il framework memorizza i batch delle migrazioni.
Appreso questo concetto, dobbiamo ripristinare il versionamento alla situazione precedente. Ci basterà invocare il comando rollback delle migrations e saranno cancellate solo le tabelle relative all’ultimo batch.
php artisan migrate:rollback
Cancelliamo il file {prefix}_create_tests_table.php e passiamo alla lezione successiva.