Migracje w Laravelu: dodanie kolumny, rollback i usuwanie

Migracje w Laravelu: dodanie kolumny, rollback i usuwanie
Szybka odpowiedź

Migrację usuwa się, kasując jej plik, ale tylko wtedy, gdy nigdy się nie wykonała. Sprawdź to komendą php artisan migrate:status: jeśli stan to Ran, uruchom najpierw php artisan migrate:rollback --step=1, a dopiero potem skasuj plik. Żeby dodać kolumnę do istniejącej tabeli, nigdy nie ruszaj starej migracji: utwórz nową z opcją --table.

Migracje wersjonują schemat bazy tak, jak Git wersjonuje kod: każda zmiana to jeden plik, odtwarzalny na dowolnym środowisku. Codzienną pracę pokrywają trzy operacje: dodanie kolumny do istniejącej tabeli, wycofanie migracji i usunięcie pliku, który nigdy nie powinien powstać.

Artykuł ze ścieżki Programowanie webowe. Wszystkie komendy i ich wyniki uruchomiono na Laravelu 13.30.1 z PHP 8.4.25.

Tworzenie migracji

Komenda trzyma się konwencji nazewnictwa, od której zależy zawartość wygenerowanego pliku:

bash
php artisan make:migration create_tasks_table

Nazwa zaczyna się od create, po którym następuje nazwa tabeli w liczbie mnogiej i table. Laravel rozpoznaje ten wzorzec i wstępnie wypełnia Schema::create() metodami id() i timestamps(), kolumna title została tu dodana dla przykładu, a wygenerowane komentarze usunięte:

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');
    }
};
Jeśli twoje migracje wyglądają inaczej

Od Laravela 8 migracje są klasami anonimowymi, zwracanymi przez return new class extends Migration. Stare migracje z nazwaną klasą, w rodzaju class CreateTasksTable extends Migration, nadal działają, ale nikt ich już nie generuje. Podobnie $table->bigIncrements('id') ustąpiło miejsca $table->id(), a metody up() i down() deklarują dziś typ zwracany void.

Jeśli tabela ma trzymać daty utworzenia i modyfikacji, dodaje je $table->timestamps(): ich brak wywołuje błąd „Unknown column 'updated_at’”.

Uruchomienie migracji tworzy tabelę:

bash
php artisan migrate

Dodanie kolumny do istniejącej tabeli

Kiedy schemat już stoi, resztę robią typowe zapytania Eloquenta. Nigdy nie zmieniaj migracji, która już się wykonała: koledzy z zespołu i serwery ją odtworzyli i drugi raz tego nie zrobią. Utwórz nową migrację i wskaż tabelę opcją --table:

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

Wygenerowany plik zawiera puste Schema::table() w obie strony. Uzupełnij oba: kolumnę w up(), jej usunięcie w 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

Warto przypomnieć tu o dwóch środkach ostrożności.

Zrób kolumnę nullowalną albo nadaj jej wartość domyślną. W tabeli, w której są już wiersze, kolumna NOT NULL bez wartości domyślnej wywala migrację: baza nie wie, co wpisać w istniejących wierszach.

Zawsze pisz down(). To ona sprawia, że migracja jest odwracalna. Migracja bez down() zamienia najdrobniejsze cofnięcie w ręczną robotę.

after() nie działa wszędzie

Ustawianie pozycji kolumny przez after() to specjalność MySQL-a i MariaDB. SQLite przyjmuje tę instrukcję bez błędu, ale ją ignoruje: kolumna ląduje na końcu tabeli. Sprawdzone na SQLite 3.46.1, gdzie notes trafia za updated_at mimo after('title'). Kolejność kolumn nie ma żadnego znaczenia funkcjonalnego.

Wycofanie migracji, która już się wykonała

Zacznij od sprawdzenia, na czym stoisz. migrate:status wypisuje każdą migrację razem z jej partią i stanem:

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

Migracja w stanie Pending nigdy się nie wykonała: jej plik możesz skasować od razu. Migrację Ran trzeba najpierw wycofać.

bash
# cofa tylko ostatnią migrację
php artisan migrate:rollback --step=1

# cofa całą ostatnią partię
php artisan migrate:rollback

Rollback uruchamia metodę down(). W powyższym przykładzie kolumna notes faktycznie znika z tabeli, a migracja wraca do stanu Pending. Dopiero wtedy, nie wcześniej, wolno usunąć plik.

Istnieją jeszcze dwie pokrewne komendy, zarezerwowane dla środowiska deweloperskiego:

bash
# cofa wszystko i odtwarza wszystko od nowa
php artisan migrate:refresh

# usuwa wszystkie tabele, a potem odtwarza wszystko od nowa
php artisan migrate:fresh
migrate:fresh kasuje dane

migrate:fresh usuwa tabele, także te, którymi nie zarządza żadna migracja. Na serwerze produkcyjnym komenda niszczy bazę i raz uruchomiona o nic już nie pyta. Trzymaj ją wyłącznie na lokalnym środowisku.

Usuwanie migracji

Nie ma czegoś takiego jak make:migration --delete: migrację usuwa się, kasując jej plik w database/migrations, czyli w katalogu zwracanym przez database_path() spośród ścieżek aplikacji. Jedyne pytanie brzmi: czy ta migracja już się wykonała.

Nigdy się nie wykonała (stan Pending): usuń plik i tyle.

bash
rm database/migrations/2026_09_02_194036_add_notes_to_tasks_table.php

Już się wykonała (stan Ran): najpierw ją wycofaj, potem usuń plik.

bash
php artisan migrate:rollback --step=1
rm database/migrations/2026_09_02_194036_add_notes_to_tasks_table.php
Jeśli migracja poszła już na inne środowisko

Skasowanie pliku nie usuwa odpowiadającego mu wiersza w tabeli migrations tych baz, na których migracja już się wykonała. Te środowiska zachowają kolumnę, której nie tłumaczy już żadna migracja. W takiej sytuacji nie usuwaj niczego: napisz nową migrację, która cofa zmianę. Zasada jest ta sama co w Gicie, gdzie zamiast przepisywać opublikowaną historię, robi się commit odwracający.

Zmiana istniejącej kolumny

Żeby zmienić typ, długość albo nullowalność, zadeklaruj kolumnę ponownie i dopisz change():

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

Dobra wiadomość dla wracających ze starszej wersji: pakiet doctrine/dbal, przez lata obowiązkowy przy tej operacji, od Laravela 11 nie jest już potrzebny. Sprawdzone na Laravelu 13.30.1, gdzie change() działa bez dodatkowej zależności.

change() przepisuje całą definicję

Atrybuty, których nie powtórzysz, przepadają. Kolumna ->nullable()->default('x') zmieniona zwykłym ->string('title', 500)->change() znów jest nienullowalna i bez wartości domyślnej. Za każdym razem deklaruj pełną definicję.

Częste błędy

Zmiana migracji, która już się wykonała Pozostałe środowiska nie odtworzą jej ponownie i zostaną przy starym schemacie. Zawsze twórz nową migrację.
Kolumna NOT NULL bez wartości domyślnej W tabeli, w której są już wiersze, migracja się wywala: baza nie wie, co wpisać w istniejących danych. Dodaj nullable() albo default().
Pominięcie metody down() Migracja staje się nieodwracalna, a każde cofnięcie wymaga ręcznej interwencji.
migrate:fresh na produkcji Komenda usuwa wszystkie tabele, także te, którymi nie zarządza żadna migracja, i o nic nie pyta.
after() ignorowane poza MySQL-em Na SQLite kolumna trafia na koniec tabeli, bez żadnego błędu. Bez skutków funkcjonalnych, ale schemat nie odpowiada temu, co napisano.
change() przepisuje całą definicję Niepowtórzone atrybuty przepadają: kolumna nullowalna z wartością domyślną traci jedno i drugie, jeśli nie zadeklarujesz ich ponownie.

LaravelMigrationsPHPSQL

Damien Flandrin Web developer od 2010 roku, twórca Gekkode i Email Impact. Każdy artykuł jest sprawdzany na prawdziwym projekcie przed publikacją. Kontakt
Newsletter

Nowe testy, poradniki i projekty, e-mailem.

Powtarzalne testy, wersjonowany kod, datowane wyniki. Nigdy spamu.