Laravel-Migrationen: Spalte hinzufügen, zurückrollen, löschen

Einleitung Spalten oder Tabellen von Hand in der Datenbank anzulegen kann einschüchternd wirken und führt meistens zu Unterschieden zwischen deinen Umgebungen. Mit Laravel-Migrationen versionierst du deine Datenbank, damit alle im Team über ein

Laravel-Migrationen: Spalte hinzufügen, zurückrollen, löschen
Schnelle Antwort

Eine Migration löschst du, indem du ihre Datei entfernst, aber nur, wenn sie nie gelaufen ist. Prüf das mit php artisan migrate:status: Steht sie auf Ran, führ zuerst php artisan migrate:rollback --step=1 aus und lösch dann die Datei. Um eine Spalte zu einer bestehenden Tabelle hinzuzufügen, änder nie die alte Migration: Leg eine neue mit --table an.

Migrationen versionieren dein Datenbankschema so, wie Git deinen Code versioniert: Jede Änderung ist eine Datei und lässt sich auf jeder Umgebung erneut abspielen. Drei Operationen decken den Alltag weitgehend ab: eine Spalte zu einer bestehenden Tabelle hinzufügen, eine Migration zurückrollen und eine Datei löschen, die nie hätte entstehen dürfen.

Artikel aus dem Themenpfad Webentwicklung. Alle Befehle und Ausgaben unten liefen auf Laravel 13.30.1 mit PHP 8.4.25.

Eine Migration anlegen

Der Befehl folgt einer Namenskonvention, die über den Inhalt der erzeugten Datei entscheidet:

bash
php artisan make:migration create_tasks_table

Der Name beginnt mit create, gefolgt vom Tabellennamen im Plural und von table. Laravel erkennt dieses Muster und füllt Schema::create() mit id() und timestamps() vor, die Spalte title ist hier für das Beispiel hinzugefügt, und die generierten Kommentare wurden entfernt:

database/migrations/2026_09_02_100000_create_tasks_table.php
<?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');
    }
};
Wenn deine Migrationen anders aussehen

Seit Laravel 8 sind Migrationen anonyme Klassen, zurückgegeben über return new class extends Migration. Ältere benannte Migrationen der Form class CreateTasksTable extends Migration funktionieren weiterhin, werden aber nicht mehr erzeugt. Ebenso hat $table->bigIncrements('id') dem $table->id() Platz gemacht, und die Methoden up() und down() deklarieren inzwischen den Rückgabetyp void.

Soll die Tabelle ein Erstellungs- und ein Änderungsdatum führen, fügt $table->timestamps() beide hinzu: Fehlen sie, bekommst du den Fehler „Unknown column ‚updated_at’“.

Die Migration auszuführen legt die Tabelle an:

bash
php artisan migrate

Eine Spalte zu einer bestehenden Tabelle hinzufügen

Steht das Schema, erledigen die gängigen Eloquent-Abfragen den Rest. Ändere nie eine Migration, die bereits gelaufen ist: Deine Kollegen und deine Server haben sie abgespielt und spielen sie kein zweites Mal. Leg eine neue Migration an und gib die betroffene Tabelle mit --table an:

bash
php artisan make:migration add_notes_to_tasks_table --table=tasks

Die erzeugte Datei enthält ein leeres Schema::table() in beide Richtungen. Füll beide: die Spalte in up(), ihr Entfernen in down().

database/migrations/2026_09_02_194036_add_notes_to_tasks_table.php
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');
    });
}
bash
php artisan migrate
code
  2026_09_02_194036_add_notes_to_tasks_table .................... 47.43ms DONE

Zwei Vorsichtsmaßnahmen sind hier eine Erinnerung wert.

Mach die Spalte nullable oder gib ihr einen Standardwert. Auf einer Tabelle, die schon Zeilen enthält, lässt eine NOT NULL-Spalte ohne Standardwert die Migration scheitern: Die Datenbank weiß nicht, was sie in die bestehenden Zeilen schreiben soll.

Schreib immer das down(). Erst dadurch wird die Migration umkehrbar. Eine Migration ohne down() macht aus dem kleinsten Rückzieher Handarbeit.

after() funktioniert nicht überall

Eine Spalte mit after() zu platzieren ist MySQL und MariaDB eigen. Unter SQLite wird die Anweisung fehlerfrei angenommen, aber ignoriert: Die Spalte landet am Ende der Tabelle. Geprüft auf SQLite 3.46.1, wo notes trotz after('title') hinter updated_at steht. Die Reihenfolge der Spalten hat keinerlei funktionale Auswirkung.

Eine bereits ausgeführte Migration zurückrollen

Sieh zuerst nach, wo du stehst. migrate:status listet jede Migration mit ihrem Batch und ihrem Zustand:

bash
php artisan migrate:status
code
  2026_09_02_100000_create_tasks_table ............................... [1] Ran
  2026_09_02_194036_add_notes_to_tasks_table ......................... Pending

Eine Migration im Zustand Pending ist nie gelaufen: Ihre Datei kannst du direkt löschen. Eine Migration im Zustand Ran muss zuerst zurückgerollt werden.

bash
# rollt nur die letzte Migration zurück
php artisan migrate:rollback --step=1

# rollt den gesamten letzten Batch zurück
php artisan migrate:rollback

Das Rollback führt die Methode down() aus. Im Beispiel oben verschwindet die Spalte notes tatsächlich aus der Tabelle, und die Migration steht wieder auf Pending. Genau dann, und keinen Moment früher, darfst du die Datei löschen.

Zwei verwandte Befehle gibt es noch, beide gehören in die Entwicklung:

bash
# rollt alles zurück und spielt alles neu ab
php artisan migrate:refresh

# löscht alle Tabellen und spielt danach alles neu ab
php artisan migrate:fresh
migrate:fresh löscht die Daten

migrate:fresh wirft die Tabellen weg, auch die, um die sich keine Migration kümmert. Auf einem Produktionsserver zerstört der Befehl die Datenbank, einmal gestartet ohne jede Rückfrage. Halte ihn strikt der lokalen Entwicklung vor.

Eine Migration löschen

Ein make:migration --delete gibt es nicht: Eine Migration löschst du, indem du ihre Datei in database/migrations entfernst, dem Verzeichnis, das database_path() unter den Pfaden der Anwendung zurückgibt. Die einzige Frage ist, ob sie schon gelaufen ist.

Sie ist nie gelaufen (Zustand Pending): Lösch die Datei, mehr nicht.

bash
rm database/migrations/2026_09_02_194036_add_notes_to_tasks_table.php

Sie ist bereits gelaufen (Zustand Ran): Roll sie zuerst zurück, dann lösch die Datei.

bash
php artisan migrate:rollback --step=1
rm database/migrations/2026_09_02_194036_add_notes_to_tasks_table.php
Wenn die Migration schon auf einer anderen Umgebung gelaufen ist

Die Datei zu löschen entfernt nicht die zugehörige Zeile in der Tabelle migrations der Datenbanken, auf denen sie bereits lief. Diese Umgebungen behalten die Spalte, ohne dass noch irgendeine Migration sie erklärt. Lösch in dem Fall gar nichts: Schreib eine neue Migration, die die Änderung rückgängig macht. Das Prinzip ist dasselbe wie bei Git, wo ein Revert-Commit dem Umschreiben bereits veröffentlichter Historie vorzuziehen ist.

Eine bestehende Spalte ändern

Um einen Typ, eine Länge oder die Nullbarkeit zu ändern, deklarierst du die Spalte erneut und hängst change() an:

php
public function up(): void
{
    Schema::table('tasks', function (Blueprint $table) {
        $table->string('title', 500)->change();
    });
}

Gute Nachricht für alle, die von einer alten Version herkommen: Das Paket doctrine/dbal, lange Pflicht für diese Operation, ist es seit Laravel 11 nicht mehr. Geprüft auf Laravel 13.30.1, wo change() ohne zusätzliche Abhängigkeit funktioniert.

change() schreibt die gesamte Definition neu

Attribute, die du nicht wiederholst, sind verloren. Eine Spalte ->nullable()->default('x'), geändert durch ein schlichtes ->string('title', 500)->change(), ist danach wieder nicht nullable und ohne Standardwert. Deklarier jedes Mal die vollständige Definition.

Häufige Fehler

Eine bereits ausgeführte Migration ändern Die anderen Umgebungen spielen sie nicht erneut ab und behalten das alte Schema. Leg immer eine neue Migration an.
NOT-NULL-Spalte ohne Standardwert Auf einer Tabelle mit vorhandenen Zeilen scheitert die Migration: Die Datenbank weiß nicht, was sie in den Bestand schreiben soll. Ergänze nullable() oder default().
Die Methode down() vergessen Die Migration wird unumkehrbar, und der kleinste Rückzieher wird zur Handarbeit.
migrate:fresh in der Produktion Der Befehl löscht alle Tabellen, auch die, um die sich keine Migration kümmert, und das ohne Rückfrage.
after() außerhalb von MySQL ignoriert Unter SQLite landet die Spalte fehlerfrei am Ende der Tabelle. Funktional folgenlos, aber das Schema entspricht nicht dem, was geschrieben steht.
change() schreibt die gesamte Definition neu Nicht wiederholte Attribute gehen verloren: Eine nullable Spalte mit Standardwert verliert beides, wenn du es nicht erneut deklarierst.

LaravelMigrationsPHPSQL

Damien Flandrin Webentwickler seit 2010, Gründer von Gekkode und Email Impact. Jeder Artikel wird vor der Veröffentlichung an einem echten Projekt getestet. Kontakt
Newsletter

Neue Tests, Tutorials und Projekte, per E-Mail.

Reproduzierbare Tests, versionierter Code, datierte Ergebnisse. Niemals Spam.