CONTATTACI

Guide per aspiranti programmatori

Lezione 18 / 30

Migrazioni del database in Laravel

Nell’ambito dei database una migrazione è un processo che permette di creare o modificare lo schema di una tabella in modo che sia possibile adattare la struttura dei dati salvati nella tabella di pari passo con le modifiche al codice che usa quelle tabelle. Potendo “versionare” la struttura delle tabelle collegandolo alla specifica versione del codice, le migrazioni facilitano le eventuali azioni di rollback di un deploy.

È importante notare che questa modalità di aggiornamento dello schema del database è facilitata nel caso in cui un database “appartenga” ad una sola applicazione, che è la sola ad avere la possibilità di modificare a piacimento la sua struttura.

Abitualmente, specie per i servizi web, la migrazione avviene nel momento in cui si effettua il deploy di una nuova versione dell’applicazione, immediatamente prima o nelle prime fasi del riavvio del servizio alla versione aggiornata.

Una migrazione “ben scritta” permette anche di effettuare l’opposto: in caso di problemi con la nuova versione, si potrebbe decidere di tornare alla versione precedente, azione indicata come rollback. In questo caso, sapendo che alla versione precedente dell’applicazione era associata una determinata “versione” dello schema del DB, sarà possibile applicare “al contrario” la migrazione.

Laravel offre pieno supporto alle migrazioni, attraverso la facade Schema e vari comandi di Artisan per creare e applicare migrazioni. Le migrazioni create sono salvate nella directory database\migration come classi PHP che estendono la classe Migration.

Creare tabelle in Laravel

La migrazione più semplice che possiamo immaginare di dover realizzare è quella in cui creiamo nuove tabelle. Nell’ottica in cui un database “appartiene” ad un’applicazione, è normale creare, nelle prime fasi di vita di un progetto, le prime tabelle necessarie a salvare e recuperare i dati necessari al progetto stesso.

Per creare una nuova migrazione è possibile affidarsi al comando Artisan make:migration.

$ php artisan make:migration create_posts_table

 INFO Migration [database/migrations/2023_01_23_193847_create_posts_table.php] created successfully.

Il file della migrazione avrà nel nome il giorno e l’ora in cui è stata creata la migrazione. Ciò è importante per determinare l’ordine con cui le migrazioni verranno applicate (prima le più vecchie, poi le più recenti).

Il nome indicato per la migrazione (create_user_table) verrà usato da Laravel per tentare di capire qual è l’intento della migrazione e la tabella che si andrà a toccare, riempiendo, quindi, in maniera opportuna il codice generato. Nel caso in esempio, il file di migrazione creato conterrà da subito quanto segue:

// database/migrations/2023_01_23_193847_create_posts_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
  /**
  * Run the migrations.
  */
  public function up()
  {
    Schema::create('posts', function (Blueprint $table) {
      $table->id();
      $table->timestamps();
    });
  }

  /**
  * Reverse the migrations.
  */
  public function down()
  {
    Schema::dropIfExists('posts');
  }
};

Una classe di migrazione contiene due metodi:

  • up: utilizzato per indicare le modifiche da applicare quando la migrazione “va su”; le modifiche ovviamente possono essere sia aggiunte di tabelle / colonne / indici, che rimozioni o cambi.
  • down: utilizzato per indicare le modifiche da applicare quando si vuole o si devono annullare le modifiche applicate dal metodo up

Laravel ha già inserito nei due metodi le azioni necessarie a creare una tabella con le colonne che ritiene necessarie o utili ($table->id() per l’id univoco di ogni riga, $table->timestamps() per le colonne created_at e updated_at).

Notare, in particolare, che il metodo down pre-generato da Laravel effettua il drop della tabella, portando alla perdita di ogni eventuale dato contenuto in essa nel caso in cui dovessimo fare il rollback. È un comportamento corretto, poiché, indicativamente, le migrazioni si interessano del tipo di dato salvato su un database, non del loro esatto contenuto. Il revert della creazione di una tabella è l’eliminazione della tabella.

A questo punto, ci è possibile aggiornare il codice della migrazione, inserendo le colonne che ci interessa avere nel nostro database, specificandone nome e tipo nel modo opportuno.

//...
      $table->id();
      $table->timestamps();
      $table->string('title');
      $table->longText('content')->nullable();
      $table->enum('status', ['draft', 'published', 'unpublished'])->default('draft');
      $table->timestamp('published_at')->nullable();
//...

Siamo, ora, pronti ad eseguire la nostra prima migrazione.

Eseguire migrazioni in Laravel

Per ogni operazione che riguardi l’esecuzione delle migrazioni, Laravel mette a disposizione il comando Artisan migrate e i suoi sotto-comandi.

L’esecuzione di questi comandi controllerà lo stato del database che è stato definito nell’attuale configurazione dell’applicazione e rispetto alle migrazioni presenti nella directory database\migration.

Per prima cosa, potremo vedere quali migrazioni sono già state applicate sul database.

 

$ php artisan migrate:status

 Migration name .......................................... Batch / Status
 2014_10_12_000000_create_users_table .................... [1] Ran
 2014_10_12_100000_create_password_resets_table .......... [1] Ran
 2019_08_19_000000_create_failed_jobs_table .............. [1] Ran
 2019_12_14_000001_create_personal_access_tokens_table ... [1] Ran
 2023_01_23_193847_create_posts_table .................... Pending

Poi, potremmo vedere quale sarà l’effettiva query che verrà eseguita sul database applicando la o le migrazioni “pending” (cioè non ancora applicate al database).

$ php artisan migrate --pretend

 INFO Running migrations.

 2023_01_23_193847_create_posts_table ....................
 ⇂ create table "posts" ("id" bigserial primary key not null, "created_at" timestamp(0) without time zone null, "updated_at" timestamp(0) without time zone null, "title" varchar(255) not null, "content" text null, "status" varchar(255) check ("status" in ('draft', 'published', 'unpublished')) not null default 'draft', "published_at" timestamp(0) without time zone null)

Infine, sarà possibile eseguire la migrazione

php artisan migrate    

 INFO Running migrations.

 2023_01_23_193847_create_posts_table .................... 3ms DONE

Una cosa, in particolare, è importante notare: il risultato dei vari comandi e la query che verrà effettivamente eseguita sul database dipendono dalla connessione al database attualmente configurata. Nel codice PHP della migrazione, infatti, sono stati definiti dei tipi di dato (longText, enum, timestamp) che non corrispondono ai tipi di colonne esattamente supportati dal proprio database. Sarà compito di Laravel convertire questa indicazione di dato nella forma più adeguata per l’attuale database.

Se, per esempio, utilizzassimo per lo sviluppo locale un database SQLite, avremmo per la stessa migrazione:

$ php artisan migrate --pretend

 INFO Running migrations.
 2023_01_23_193847_create_posts_table ....................

 ⇂ create table "posts" ("id" integer not null primary key autoincrement, "created_at" datetime, "updated_at" datetime, "title" varchar not null, "content" text, "status" varchar check ("status" in ('draft', 'published', 'unpublished')) not null default 'draft', "published_at" datetime)

NOTA: Laravel mette a disposizione molti metodi dedicati per tipo di dato con cui definire una colonna, per un elenco completo e le varie opzioni disponibili per ogni tipo consultare la documentazione alla pagina.

Una particolarità del comando migrate:rollback è quella di poter indicare quante migrazioni “indietro” si vuole tornare, tramite l’opzione –step=<NUM>.

Primary key e foreign key in Laravel

Nella creazione di tabelle e colonne è possibile dover impostare primary key e foreign key.

Le migrazioni Laravel offrono due modi per creare la colonna che ospita una primary key:

  • tramite uno dei metodi “id” dedicati, come ad esempio $table->id() che crea una colonna di nome id impostandola come primaria
  • tramite il metodo primary, che aggiunge una chiava primaria alla colonna indicato $table->primary(‘my_id’)

Anche la creazione di foreign key è resa semplice:

 

Schema::table('posts', function (Blueprint $table) {
  // creo colonna nella tabella posts, senza chiave
  $table->unsignedBigInteger('user_id');

  // associo la nuova colonna alla colonna esistente nell'altra tabella
  $table->foreign('user_id')->references('id')->on('users');
});

Modificare tabelle e colonne in Laravel

Le migrazioni seguono l’evoluzione dell’applicazione; per questo, oltre a creare nuove tabelle, è possibile modificare tabelle e colonne già esistenti.

NOTA: per poter modificare una colonna, è richiesto l’aggiunta del pacchetto doctrine/dbal alla propria applicazione tramite Composer. Questo pacchetto serve per creare la o le query di modifica della colonna in base allo stato attuale.

// ...

Schema::table('users', function (Blueprint $table) {

  // cambia il numero di caratteri massimi della colonna title e la rende nullabile
  // notare il change() usato alla fine della catena di impostazioni
  $table->string('title', 50)->nullable()->change();

  // rename di una colonna, from -> to
  $table->renameColumn('status', 'publishing_status');
});

Nel momento in cui si cambia una colonna esistente è necessario aggiornare anche tutte le altre parti del codice dell’applicazione in cui quella colonna era utilizzata, per esempio nelle varie query viste nella lezione precedente. La creazione di una migrazione di modifica e le opportune modifiche al codice vanno viste come un tutt’uno, non come due step successivi. Sarà compito del sistema di deploy scelto occuparsi dell’effettivo ordine di avvio.

Eseguire rollback delle migrazioni in Laravel

Anche per rollback delle migrazioni è possibile usare il relativo comando Artisan migrate:rollback, eventualmente con l’opzione –pretend per vedere l’esatta query che verrà eseguita.

$ php artisan migrate:rollback --pretend

 INFO Rolling back migrations.

 2023_01_23_193847_create_posts_table ....................
 ⇂ drop table if exists "posts"

$ php artisan migrate:rollback    

 INFO Rolling back migrations.

 2023_01_23_193847_create_posts_table .................... 4ms DONE

È importante non dimenticare che durante il rollback viene eseguito il codice fornito nel metodo down ed è, quindi, necessario fornire una contro-modifica coerente con quanto applicato dal metodo up. Supponendo di voler cambiare nome e tipo alla colonna title della nostra tabella posts

public function up()
  {
    Schema::table('posts', function (Blueprint $table) {
      $table->tinyText('title')->change();
      $table->renameColumn('title', 'subject');
    });
  }

  public function down()
  {
    Schema::table('posts', function (Blueprint $table) {
      $table->string('subject')->change();
      $table->renameColumn('subject', 'title');
    });
  }

Per poter riportare il database nella situazione precedente è necessario trovare la corretta sequenza di azioni lato codice che Laravel può convertire in query SQL. In questo caso, nella down è necessario prima cambiare il tipo di colonna e poi cambiare nome, riportandolo a quello precedente.

Una sequenza concettualmente simile potrebbe non essere applicabile (ed è per questo che è sempre doveroso sia applicare che fare rollback delle migrazioni in fase di sviluppo.)

// questa sequenza di azioni nella down porta a un errore nel rollback
// non è possibile tornare allo stato precedente
$table->renameColumn('subject', 'title');
$table->string('title')->change();

Contattaci senza impegno per informazioni sul corso

Pagamento rateale

Valore della rata: A PARTIRE DA 115 €/mese.

Esempio di finanziamento 

Importo finanziato: € 2440 in 24 rate da € 115 – TAN fisso 9,55% TAEG 12,57% – importo totale del credito € 2841.

Il costo totale del credito comprende: interessi calcolati al TAN indicato, oneri fiscali (imposta di bollo sul contratto 16,00 euro*) addebitati sulla prima rata, costo mensile di gestione pratica € 3,90, spesa di istruttoria € 0,00, spesa per invio rendicontazione periodica cartacea € 0,98 (o spesa per invio rendicontazione periodica cartacea € 0,00), imposta di bollo su rendicontazione periodica € 0,00. Modalità di rimborso obbligatoria: addebito diretto su c/c. La scadenza delle rate è determinata dal giorno della liquidazione del contratto; la data di scadenza delle rate è prevista il giorno 15 del mese. L’importo di ciascuna rata comprende una quota di capitale crescente e interessi decrescente secondo un piano di ammortamento “alla francese”. Offerta valida dal 01/01/2024 al 31/12/2024.

Messaggio pubblicitario con finalità promozionale. Per le informazioni precontrattuali richiedere sul punto vendita il documento “Informazioni europee di base sul credito ai consumatori” (SECCI) e copia del testo contrattuale. Salvo approvazione di Sella Personal Credit S.p.A. Aulab S.r.l. opera quale intermediario del credito NON in esclusiva.

*In fase di richiesta del finanziamento verrà proposta la facoltà di selezionare, in alternativa all’imposta di bollo sul contratto di 16,00 euro, l’imposta sostitutiva, pari allo 0,25% dell’importo finanziato.

Pagamento rateale

Valore della rata: A PARTIRE DA 210 €/mese.

Esempio di finanziamento  

Importo finanziato: € 4500 in 24 rate da € 210,03 – TAN fisso 9,68% TAEG 11,97% – importo totale del credito € 5146,55.

Il costo totale del credito comprende: interessi calcolati al TAN indicato, oneri fiscali (imposta di bollo sul contratto 16,00 euro*) addebitati sulla prima rata, costo mensile di gestione pratica € 3,90, spesa di istruttoria € 0,00, spesa per invio rendicontazione periodica cartacea € 0,98 (o spesa per invio rendicontazione periodica cartacea € 0,00), imposta di bollo su rendicontazione periodica € 0,00. Modalità di rimborso obbligatoria: addebito diretto su c/c. La scadenza delle rate è determinata dal giorno della liquidazione del contratto; la data di scadenza delle rate è prevista il giorno 15 del mese. L’importo di ciascuna rata comprende una quota di capitale crescente e interessi decrescente secondo un piano di ammortamento “alla francese”. Offerta valida dal 01/01/2024 al 31/12/2024.

Messaggio pubblicitario con finalità promozionale. Per le informazioni precontrattuali richiedere sul punto vendita il documento “Informazioni europee di base sul credito ai consumatori” (SECCI) e copia del testo contrattuale. Salvo approvazione di Sella Personal Credit S.p.A. Aulab S.r.l. opera quale intermediario del credito NON in esclusiva.

* In fase di richiesta del finanziamento verrà proposta la facoltà di selezionare, in alternativa all’imposta di bollo sul contratto di 16,00 euro, l’imposta sostitutiva, pari allo 0,25% dell’importo finanziato.

Contattaci senza impegno per informazioni sul corso

Scopriamo insieme se i nostri corsi fanno per te. Compila il form e aspetta la chiamata di uno dei nostri consulenti.