Migration adalah cara Laravel melacak perubahan struktur database dalam bentuk kode, sehingga tim Anda selalu punya skema yang sama dan perubahan bisa dibalik jika salah. Tetapi migration yang ceroboh — terutama di produksi — bisa menyebabkan kehilangan data permanen. Artikel ini membahas cara membuat migration yang aman, dari dasar hingga strategi menjalankan migrasi di server produksi tanpa merusak data pengguna.
1. Membuat Tabel Baru
Setiap migration punya method up() (menerapkan perubahan) dan down() (membatalkannya). Buat migration dengan artisan:
php artisan make:migration create_products_table
public function up(): void
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->integer('price');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('products');
}
Selalu isi down() dengan benar. Method inilah yang membuat rollback aman.
2. Mengubah Tabel yang Sudah Ada
Jangan pernah mengedit file migration lama yang sudah dijalankan di produksi. Sebagai gantinya, buat migration BARU untuk setiap perubahan:
php artisan make:migration add_stock_to_products_table --table=products
public function up(): void
{
Schema::table('products', function (Blueprint $table) {
$table->integer('stock')->default(0)->after('price');
});
}
public function down(): void
{
Schema::table('products', function (Blueprint $table) {
$table->dropColumn('stock');
});
}
Untuk mengganti nama atau tipe kolom, pastikan package doctrine/dbal terpasang jika Anda memakai Laravel versi lama. Di Laravel 11, change() sudah didukung tanpa dependensi tambahan.
3. Foreign Key yang Benar
Foreign key menjaga integritas relasi antar tabel. Cara paling ringkas di Laravel modern:
Schema::create('orders', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')
->constrained()
->onDelete('cascade');
$table->timestamps();
});
constrained() otomatis menautkan ke tabel users berdasarkan konvensi. onDelete('cascade') menghapus order saat user-nya dihapus. Gunakan dengan hati-hati — kadang nullOnDelete() lebih aman agar data tidak ikut terhapus.
4. Menghindari Kehilangan Data
Beberapa operasi bersifat merusak dan tidak bisa dibatalkan. Perhatikan hal berikut:
- dropColumn menghapus kolom beserta seluruh isinya secara permanen. Pastikan datanya benar-benar tidak dibutuhkan.
- Saat menambah kolom
NOT NULLke tabel yang sudah berisi data, wajib beridefault()atau jadikannullable(). Tanpa itu migration akan gagal. - Sebelum migration destruktif di produksi, selalu backup database terlebih dahulu.
// Aman: kolom baru punya nilai default
$table->string('status')->default('pending');
// atau
$table->string('phone')->nullable();
5. Menjalankan Migrasi di Produksi
Di lokal Anda memakai php artisan migrate. Di produksi, Laravel akan meminta konfirmasi karena khawatir merusak data. Untuk deployment otomatis, gunakan flag --force:
php artisan migrate --force
Selalu jalankan ini setelah backup, dan idealnya dalam mode maintenance:
php artisan down # aktifkan mode maintenance
php artisan migrate --force
php artisan up # kembali online
Untuk membatalkan batch migration terakhir jika terjadi masalah:
php artisan migrate:rollback
6. Tips Zero-Downtime
Menambah kolom besar atau index pada tabel jutaan baris bisa mengunci tabel dan membuat aplikasi tidak responsif. Beberapa strategi:
- Pisahkan perubahan bertahap: tambahkan kolom baru (nullable) di satu deploy, isi datanya, baru terapkan constraint di deploy berikutnya.
- Hindari mengubah kolom yang sedang dipakai kode lama: pastikan kode dan skema kompatibel di kedua arah selama transisi.
- Untuk MySQL besar, pertimbangkan tool seperti
pt-online-schema-changeagar perubahan tidak mengunci tabel.
7. Seeder untuk Data Awal
Seeder mengisi database dengan data awal atau data contoh. Buat dengan artisan:
php artisan make:seeder RoleSeeder
public function run(): void
{
Role::insert([
['name' => 'admin'],
['name' => 'member'],
]);
}
Jalankan seeder tanpa mengulang seluruh migrasi:
php artisan db:seed --class=RoleSeeder
Untuk data referensi yang harus ada di produksi (misalnya daftar role atau kategori), gunakan updateOrInsert() di dalam seeder agar aman dijalankan berulang tanpa membuat duplikat.
8. Memeriksa Status Migrasi
Sebelum menjalankan migrasi di server, penting mengetahui migration mana yang sudah dan belum dijalankan. Laravel menyediakan perintah untuk itu:
php artisan migrate:status
Perintah ini menampilkan daftar migration beserta tanda apakah sudah dijalankan (Ran) atau belum (Pending). Kebiasaan mengeceknya sebelum deploy mencegah Anda menjalankan migration yang tidak sengaja tertinggal atau menjalankan ulang yang sudah diterapkan.
Hindari juga perintah berikut di produksi kecuali Anda benar-benar yakin, karena akan menghapus SEMUA tabel lalu membuat ulang dari nol:
php artisan migrate:fresh // BERBAHAYA di produksi: menghapus semua data
php artisan migrate:refresh // rollback semua lalu migrate ulang
Kedua perintah ini sangat berguna saat pengembangan lokal, tetapi di produksi cukup pakai migrate --force yang hanya menjalankan migration baru tanpa menyentuh data yang sudah ada.
Kesimpulan
Migration yang aman berpijak pada beberapa aturan sederhana: jangan pernah mengedit migration lama, selalu isi down(), beri nilai default saat menambah kolom NOT NULL, dan backup sebelum operasi destruktif di produksi. Periksa migrate:status sebelum deploy, dan jauhi migrate:fresh di server hidup. Dengan migrate --force di dalam mode maintenance serta strategi bertahap untuk tabel besar, Anda bisa mengembangkan skema database tanpa takut kehilangan data pengguna.
Strategi Rollback dan Pencadangan
Migrasi yang aman selalu menyiapkan jalan mundur. Sebelum menjalankan migrasi di lingkungan produksi, buat cadangan penuh basis data agar Anda dapat memulihkan kondisi bila terjadi kegagalan. Rancang setiap migrasi agar dapat dibatalkan melalui metode down yang benar, sehingga perubahan bisa digulung balik tanpa merusak data. Untuk perubahan besar, uji terlebih dahulu di lingkungan staging yang menyerupai produksi, lalu jalankan pada jam sepi untuk meminimalkan dampak ke pengguna. Hindari menghapus kolom atau tabel secara langsung; lebih aman menonaktifkannya dulu, memastikan tidak ada yang bergantung padanya, baru menghapus di migrasi terpisah. Dengan disiplin pencadangan dan rollback, migrasi database menjadi proses yang terkendali dan bebas kepanikan meski terjadi hal tak terduga.