
Une migration se supprime en effaçant son fichier, mais seulement si elle n’a jamais tourné. Vérifiez avec php artisan migrate:status : si elle est Ran, lancez d’abord php artisan migrate:rollback --step=1, puis supprimez le fichier. Pour ajouter une colonne à une table existante, ne modifiez jamais l’ancienne migration : créez-en une nouvelle avec --table.
Les migrations versionnent votre schéma de base comme Git versionne votre code : chaque changement est un fichier, rejouable sur n’importe quel environnement. Trois opérations couvrent l’essentiel du quotidien : ajouter une colonne à une table qui existe déjà, annuler une migration, et supprimer un fichier qu’on n’aurait pas dû créer.
Article du parcours Développement web. Toutes les commandes et sorties ci-dessous ont été exécutées sur Laravel 13.30.1 avec PHP 8.4.25.
Créer une migration
La commande suit une convention de nommage qui détermine le contenu du fichier généré :
php artisan make:migration create_tasks_tableLe nom commence par create, suivi du nom de la table au pluriel, suivi de table. Laravel reconnaît ce motif et pré-remplit Schema::create() avec id() et timestamps(), la colonne title est ajoutée ici pour l’exemple, et les commentaires générés ont été retirés :
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('tasks', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('tasks');
}
};Depuis Laravel 8, les migrations sont des classes anonymes retournées par return new class extends Migration. Les anciennes migrations nommées, du type class CreateTasksTable extends Migration, continuent de fonctionner mais ne sont plus générées. De même, $table->bigIncrements('id') a laissé la place à $table->id(), et les méthodes up() et down() déclarent désormais un type de retour void.
Si la table doit porter des dates de création et de modification, $table->timestamps() les ajoute : leur absence provoque l’erreur « Unknown column ‘updated_at’ ».
Exécuter la migration crée la table :
php artisan migrateAjouter une colonne à une table existante
Une fois le schéma en place, les requêtes Eloquent courantes font le reste. Ne modifiez jamais une migration déjà exécutée : vos collègues et vos serveurs l’ont jouée, ils ne la rejoueront pas. Créez une nouvelle migration, en indiquant la table concernée avec --table :
php artisan make:migration add_notes_to_tasks_table --table=tasksLe fichier généré contient un Schema::table() vide dans les deux sens. Remplissez-les : la colonne dans up(), sa suppression dans down().
public function up(): void
{
Schema::table('tasks', function (Blueprint $table) {
$table->text('notes')->nullable()->after('title');
});
}
public function down(): void
{
Schema::table('tasks', function (Blueprint $table) {
$table->dropColumn('notes');
});
}php artisan migrate 2026_09_02_194036_add_notes_to_tasks_table .................... 47.43ms DONEDeux précautions valent d’être rappelées ici.
Rendez la colonne nullable, ou donnez-lui une valeur par défaut. Sur une table qui contient déjà des lignes, une colonne NOT NULL sans valeur par défaut fait échouer la migration : la base ne sait pas quoi mettre dans les lignes existantes.
Écrivez toujours le down(). C’est lui qui rend la migration réversible. Une migration sans down() transforme le moindre retour arrière en intervention manuelle.
Positionner une colonne avec after() est propre à MySQL et MariaDB. Sur SQLite, l’instruction est acceptée sans erreur mais ignorée : la colonne est ajoutée en fin de table. Vérifié sur SQLite 3.46.1, où notes se retrouve après updated_at malgré le after('title'). L’ordre des colonnes n’a aucune incidence fonctionnelle.
Annuler une migration déjà exécutée
Commencez par regarder où vous en êtes. migrate:status liste chaque migration avec son lot et son état :
php artisan migrate:status 2026_09_02_100000_create_tasks_table ............................... [1] Ran
2026_09_02_194036_add_notes_to_tasks_table ......................... PendingUne migration Pending n’a jamais été jouée : son fichier peut être supprimé directement. Une migration Ran doit d’abord être annulée.
# annule la dernière migration seulement
php artisan migrate:rollback --step=1
# annule tout le dernier lot
php artisan migrate:rollbackLe rollback exécute la méthode down(). Sur l’exemple précédent, la colonne notes disparaît bien de la table et la migration repasse en Pending. C’est à ce moment, et pas avant, que vous pouvez supprimer le fichier.
Deux commandes voisines existent, à réserver au développement :
# annule tout et rejoue tout
php artisan migrate:refresh
# supprime toutes les tables, puis rejoue tout
php artisan migrate:freshmigrate:fresh supprime les tables, y compris celles qu’aucune migration ne gère. Sur un serveur de production, la commande détruit la base sans confirmation possible une fois lancée. Réservez-la strictement au développement local.
Supprimer une migration
Il n’existe pas de make:migration --delete : une migration se supprime en effaçant son fichier, dans database/migrations, dossier que renvoie database_path() parmi les chemins de l’application. La seule question est de savoir si elle a déjà tourné.
Elle n’a jamais été exécutée (état Pending) : supprimez le fichier, rien d’autre.
rm database/migrations/2026_09_02_194036_add_notes_to_tasks_table.phpElle a déjà été exécutée (état Ran) : annulez-la d’abord, puis supprimez le fichier.
php artisan migrate:rollback --step=1
rm database/migrations/2026_09_02_194036_add_notes_to_tasks_table.phpSupprimer le fichier n’efface pas la ligne correspondante dans la table migrations des bases où elle a déjà tourné. Ces environnements garderont la colonne, sans plus aucune migration qui l’explique. Dans ce cas, ne supprimez rien : écrivez une nouvelle migration qui défait le changement. Le principe est le même qu’avec Git, où l’on préfère un commit d’annulation à une réécriture d’historique déjà publié.
Modifier une colonne existante
Pour changer un type, une longueur ou la nullabilité, redéclarez la colonne et ajoutez change() :
public function up(): void
{
Schema::table('tasks', function (Blueprint $table) {
$table->string('title', 500)->change();
});
}Bonne nouvelle pour qui revient d’une version ancienne : le paquet doctrine/dbal, longtemps obligatoire pour cette opération, ne l’est plus depuis Laravel 11. Vérifié sur Laravel 13.30.1, où change() fonctionne sans dépendance supplémentaire.
Les attributs que vous ne répétez pas sont perdus. Une colonne ->nullable()->default('x') modifiée par un simple ->string('title', 500)->change() redevient non nullable et sans valeur par défaut. Redéclarez la définition complète à chaque fois.
Erreurs fréquentes
nullable() ou default().

