Tabel posts kita baru berumur beberapa hari, dan kebutuhan sudah berubah: tim ingin setiap artikel punya ringkasan singkat yang bisa ditampilkan di daftar artikel, dan nama kolom body terasa kurang jelas dibanding content. Perubahan seperti ini adalah makanan sehari-hari pengembangan perangkat lunak, dan migration menyediakan jalur yang aman untuk melakukannya. Pada chapter ini kita menambahkan kolom baru, mengubah panjang kolom lama, mengganti nama kolom, lalu memverifikasi semuanya dengan db:table.
Satu Perubahan, Satu Migration Baru #
Prinsip pertama yang wajib dipegang: jangan pernah mengedit migration yang sudah dijalankan. Tabel migrations mencatat nama berkas yang sudah dieksekusi, dan php artisan migrate hanya menjalankan berkas yang belum tercatat — mengedit isi create_posts_table tidak akan mengubah tabel posts di database mana pun yang sudah memigrasinya. Perbaikannya: setiap perubahan struktural ditulis sebagai migration baru, sehingga rekan tim dan server produksi cukup menjalankan migrate untuk mendapatkan perubahan yang sama.
Untuk menambah kolom pada tabel yang sudah ada, konvensi penamaannya adalah pola add_*_to_*_table plus opsi --table yang menyebut tabel sasaran:
php artisan make:migration add_excerpt_to_posts_table --table=postsOutput yang Dihasilkan:
INFO Migration [database/migrations/2026_10_13_170257_add_excerpt_to_posts_table.php] created successfully.Karena pola nama dan opsi --table memberi tahu Laravel bahwa kita mengubah tabel posts (bukan membuat tabel baru), stub yang dihasilkan memakai Schema::table.
Schema::table untuk Tabel yang Sudah Ada #
Perbedaan dua method ini adalah kunci chapter ini:
| Method | Kapan dipakai | Operasi khas |
|---|---|---|
Schema::create('posts', ...) | Membuat tabel baru | Mendefinisikan seluruh kolom dari nol |
Schema::table('posts', ...) | Mengubah tabel yang sudah ada | Menambah, mengubah, mengganti nama, atau menghapus kolom |
Callback pada Schema::table menerima objek Blueprint yang sama, tetapi setiap pemanggilan method kini berarti “ubah tabel ini” alih-alih “definisikan tabel baru”.
Menambah dan Mengubah Kolom #
Kita isi migration baru dengan dua operasi sekaligus — menambah kolom excerpt dan memperpanjang kolom title:
public function up(): void
{
Schema::table('posts', function (Blueprint $table) {
$table->string('excerpt', 200)->nullable();
$table->string('title', 200)->change();
});
}
public function down(): void
{
Schema::table('posts', function (Blueprint $table) {
$table->dropColumn('excerpt');
$table->string('title', 150)->change();
});
}Bedah barisnya: $table->string('excerpt', 200)->nullable() mendefinisikan kolom baru excerpt bertipe varchar(200). Modifier ->nullable() penting — tabel posts mungkin sudah berisi baris, dan kolom baru tanpa nilai default akan gagal diterapkan pada baris-baris lama jika dipaksa tidak boleh NULL. Panggilan $table->string('title', 200)->change() berbeda: method ->change() memberi tahu Laravel bahwa definisi di depannya adalah kondisi akhir kolom title, bukan kolom baru — kolom lama varchar(150) akan diubah menjadi varchar(200).
Method down() menulis kebalikan yang simetris: dropColumn('excerpt') membuang kolom yang ditambahkan, dan ->change() mengembalikan title ke panjang semula. Aturan praktisnya mudah: setiap baris di up() harus punya pasangan kebalikan di down().
Mari jalankan:
php artisan migrateOutput yang Dihasilkan:
INFO Running migrations.
2026_10_13_170257_add_excerpt_to_posts_table ................... 4.66ms DONEMengganti Nama Kolom #
Perubahan kedua — body menjadi content — kita tulis sebagai migration terpisah agar setiap berkas hanya memuat satu maksud:
php artisan make:migration rename_body_to_content_in_posts_table --table=postsOutput yang Dihasilkan:
INFO Migration [database/migrations/2026_10_13_170304_rename_body_to_content_in_posts_table.php] created successfully.Isinya satu operasi ganti nama di setiap arah:
public function up(): void
{
Schema::table('posts', function (Blueprint $table) {
$table->renameColumn('body', 'content');
});
}
public function down(): void
{
Schema::table('posts', function (Blueprint $table) {
$table->renameColumn('content', 'body');
});
}Method renameColumn() menerima dua argumen: nama kolom lama dan nama kolom baru. Tipe dan isi kolom tidak tersentuh — yang berubah hanya labelnya, jadi data artikel yang sudah ada tetap utuh. Setelah menjalankan migrate sekali lagi, kedua perubahan kita telah diterapkan.
Verifikasi dengan db:table #
Perintah db:table menutup siklus dengan bukti struktur akhir:
php artisan db:table postsOutput yang Dihasilkan:
main.posts .................................................................
Columns .................................................................. 6
Column ................................................................ Type
id integer, autoincrement .......................................... integer
title varchar ...................................................... varchar
content text .......................................................... text
created_at datetime, nullable ..................................... datetime
updated_at datetime, nullable ..................................... datetime
excerpt varchar, nullable .......................................... varchar
Index ......................................................................
primary id ......................................................... primary Struktur baru telah lengkap: kolom content menggantikan body, dan excerpt hadir sebagai kolom nullable. Perhatikan posisinya di paling bawah — kolom tambahan selalu diletakkan setelah kolom-kolom yang ada; SQLite tidak mendukung penyisipan kolom di posisi tertentu, dan urutan kolom memang tidak punya makna fungsional dalam SQL.
Perilaku Khusus SQLite #
Seluruh operasi di atas berhasil mulus di SQLite, tetapi ada hal menarik di balik layar. Perintah ALTER TABLE asli SQLite hanya mendukung perubahan terbatas — menambah kolom dan mengganti nama. Untuk operasi ->change(), Laravel melakukan table rebuild: membuat tabel sementara, menyalin data, lalu menggantikan tabel lama. Berikut kumpulan SQL yang benar-benar dijalankan Laravel untuk ->change() pada tabel posts, yang kita tangkap lewat query log:
create table "__temp__posts" ("id" integer primary key autoincrement not null, ...)
insert into "__temp__posts" ("id", "title", ...) select "id", "title", ... from "posts"
drop table "posts"
alter table "__temp__posts" rename to "posts"Dengan dua migration baru ini, sejarah perubahan skema kita rapi: satu berkas satu maksud, dan semuanya bisa dibatalkan. Pertanyaan berikutnya adalah bagaimana proses pembatalan itu bekerja — itulah topik chapter Migration Rollback.