Bayangkan tim berisi lima developer. Setiap orang mengubah struktur database di komputernya sendiri lewat phpMyAdmin. Seminggu kemudian tidak ada yang tahu struktur mana yang benar, dan deploy ke server berakhir error karena kolom yang hilang. Migration menyelesaikan masalah ini: struktur database ditulis sebagai kode, disimpan di Git, dan dijalankan berurutan seperti riwayat perubahan.
Apa itu migration?
Migration adalah file PHP yang berisi dua hal:
up(): perubahan yang ingin diterapkan (buat tabel, tambah kolom).down(): cara membatalkan perubahan itu.
Laravel mencatat migration mana yang sudah dijalankan di tabel migrations, sehingga setiap perubahan hanya diterapkan sekali dan selalu berurutan berdasarkan nama file (yang diawali timestamp).
Membuat migration
php artisan make:migration create_produk_table
Laravel menebak dari namanya: awalan create_..._table membuat kerangka pembuatan tabel. Isinya kira-kira:
public function up(): void
{
Schema::create('produk', function (Blueprint $table) {
$table->id();
$table->string('nama', 100);
$table->text('deskripsi')->nullable();
$table->decimal('harga', 12, 2)->default(0);
$table->unsignedInteger('stok')->default(0);
$table->boolean('aktif')->default(true);
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('produk');
}
Cara pintas yang sering dipakai: php artisan make:model Produk -m membuat model sekaligus migration-nya.
Menjalankan dan membatalkan
| Perintah | Fungsi |
|---|---|
php artisan migrate | Menjalankan semua migration yang belum dijalankan |
php artisan migrate:status | Melihat migration mana yang sudah/ belum jalan |
php artisan migrate:rollback | Membatalkan batch terakhir |
php artisan migrate:rollback --step=2 | Membatalkan 2 batch terakhir |
php artisan migrate:fresh | Menghapus semua tabel lalu menjalankan ulang migration |
php artisan migrate:fresh --seed | Sama seperti di atas, lalu mengisi data awal |
Peringatan:migrate:freshdanmigrate:refreshmenghapus data. Pakai hanya di lingkungan lokal. Di produksi, gunakanphp artisan migrate --forceyang hanya menambah perubahan baru.
Tipe kolom yang sering dipakai
| Method | Tipe di MySQL |
|---|---|
id() | BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY |
string('nama', 100) | VARCHAR(100) |
text('isi') / longText() | TEXT / LONGTEXT |
integer(), bigInteger(), unsignedInteger() | INT, BIGINT, INT UNSIGNED |
decimal('harga', 12, 2) | DECIMAL(12,2): gunakan untuk uang |
boolean('aktif') | TINYINT(1) |
date(), dateTime(), timestamp() | DATE, DATETIME, TIMESTAMP |
json('opsi') | JSON |
enum('status', ['draft','terbit']) | ENUM |
timestamps() | created_at dan updated_at |
softDeletes() | deleted_at untuk soft delete |
Pemodifikasi: ->nullable(), ->default(x), ->unique(), ->index(), ->after('kolom'), ->comment('...').
Mengubah tabel yang sudah ada
Jangan mengedit migration lama yang sudah dijalankan oleh orang lain. Buat migration baru:
php artisan make:migration add_slug_to_produk_table --table=produk
public function up(): void
{
Schema::table('produk', function (Blueprint $table) {
$table->string('slug')->unique()->after('nama');
});
}
public function down(): void
{
Schema::table('produk', function (Blueprint $table) {
$table->dropColumn('slug');
});
}
Pada Laravel 10 ke bawah, mengubah tipe kolom yang sudah ada (->change()) membutuhkan paket doctrine/dbal. Mulai Laravel 11 perubahan kolom sudah didukung langsung.
Menambah kolom NOT NULL pada tabel yang sudah berisi data memerlukan nilai bawaan (->default(...)) atau dibuat nullable() dulu, kalau tidak migration akan gagal.
Index dan foreign key
Schema::create('pesanan', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('status', 20)->index();
$table->decimal('total', 12, 2);
$table->timestamps();
$table->index(['user_id', 'status']); // index gabungan
});
foreignId('user_id')->constrained() otomatis membuat kolom bertipe sama dan constraint ke tabel users (Laravel menebak nama tabel dari nama kolom). cascadeOnDelete(), restrictOnDelete(), dan nullOnDelete() mengatur aksi saat induk dihapus. Konsep dasarnya dibahas di Memahami Primary Key dan Foreign Key.
Urutan penting: tabel yang dirujuk harus dibuat lebih dulu (timestamp migration-nya lebih awal).
Seeder dan factory
Seeder mengisi data awal atau data contoh:
php artisan make:seeder KategoriSeeder
php artisan db:seed --class=KategoriSeeder
Factory membuat data palsu realistis untuk pengembangan dan testing:
Produk::factory()->count(50)->create();
Praktik terbaik
- Satu migration, satu tujuan. Lebih mudah dibaca dan di-rollback.
- Selalu tulis
down()yang benar agar rollback aman. - Jangan mengubah migration yang sudah di-merge ke cabang utama. Tambah migration baru.
- Pisahkan perubahan skema dari perubahan data. Migrasi data besar sebaiknya memakai command atau seeder tersendiri.
- Uji di salinan data produksi sebelum menjalankan migration berisiko (mengubah tabel jutaan baris bisa mengunci tabel).
- Backup sebelum deploy yang memuat migration destruktif (
dropColumn,dropTable). - Commit migration bersama kode yang memakainya.
Masalah umum
| Gejala | Penyebab |
|---|---|
Base table or view already exists | Tabel dibuat manual atau migration dijalankan sebagian. Periksa migrate:status |
Cannot add foreign key constraint | Tipe kolom FK tidak sama dengan PK, atau tabel induk belum ada |
Specified key was too long (MySQL lama) | Atur Schema::defaultStringLength(191) di AppServiceProvider |
Class not found saat migrate | Nama class tidak cocok dengan nama file, jalankan composer dump-autoload |
Kesimpulan
Migration menjadikan skema database sebagai bagian dari kodebase: terversi, bisa di-review, dan bisa direproduksi di komputer mana pun. Buat migration baru untuk setiap perubahan, tulis down() dengan benar, dan jaga agar perintah destruktif tidak pernah menyentuh produksi tanpa backup.
Komentar (0)
Masuk / Daftar