Migrasi Database — Laravel

Migration adalah version control untuk database — melacak perubahan skema secara terstruktur. Membuat migration: php artisan make:migration create_users_table…

Migration adalah file PHP yang menulis satu perubahan struktur database: bikin tabel, tambah kolom, hapus kolom. Laravel menjalankan file-file itu berurutan dan mencatat mana yang sudah jalan, jadi database di laptopmu, di laptop temanmu, dan di server punya tabel yang sama persis. Bayangin buku resep renovasi yang langkahnya bernomor, plus daftar centang di dinding. Tukangnya cuma mengerjakan langkah yang belum dicentang, lalu mencentangnya. Langkah yang sudah dicentang gak dikerjakan lagi, walau tulisan di resepnya kamu ubah. Di Laravel, tukangnya adalah Artisan, dan daftar centangnya adalah tabel bernama migrations.

🧪 Tebakan cepat: kamu sudah menjalankan php artisan migrate untuk tabel books. Lalu kamu menghapus ->nullable() dari kolom isbn di file yang sama, dan menjalankan migrate lagi. Sekarang isbn wajib diisi? Tebak dulu, jawabannya ada di bagian "Laravel cuma mencocokkan nama file".

Membuat file migration

Sampai pelajaran Blade, BookController menyimpan buku di array PHP, jadi buku baru hilang begitu request selesai. Sekarang buku pindah ke database, ke tabel books. Buat file-nya dengan Artisan:

php artisan make:migration create_books_table
   INFO  Migration [/home/budi/perpustakaan/database/migrations/2026_10_09_090000_create_books_table.php] created successfully.

Isi file: up() dan down()

<?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('books', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->string('author');
            $table->integer('stock')->default(0);
            $table->string('isbn')->nullable();
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('books');
    }
};

Laravel menerjemahkan Blueprint ke SQL sesuai database-nya. Ini hasil persis Laravel 13:

Blueprint MySQL SQLite
$table->id() bigint unsigned not null auto_increment primary key integer primary key autoincrement not null
$table->string('title') varchar(255) not null varchar not null
->integer('stock')->default(0) int not null default '0' integer not null default '0'
->string('isbn')->nullable() varchar(255) null varchar
->boolean('borrowable')->default(true) tinyint(1) not null default '1' tinyint(1) not null default '1'
$table->timestamps() created_at dan updated_at, timestamp null created_at dan updated_at, datetime

Menjalankan: migrate, status, rollback

$ php artisan migrate

   INFO  Preparing database.

  Creating migration table ....................................... 3.51ms DONE

   INFO  Running migrations.

  2026_10_09_090000_create_books_table ........................... 1.99ms DONE

$ php artisan migrate:status

  Migration name .............................................. Batch / Status
  2026_10_09_090000_create_books_table ............................... [1] Ran

$ php artisan migrate:rollback

   INFO  Rolling back migrations.

  2026_10_09_090000_create_books_table ........................... 0.85ms DONE

Laravel cuma mencocokkan nama file

Sebelum menjalankan apa pun, migrate membaca nama-nama di tabel migrations. File yang namanya sudah ada di sana dilewati, apa pun isinya sekarang. Jadi menghapus ->nullable() lalu migrate lagi cuma menjawab:

   INFO  Nothing to migrate.

Kolom isbn di database tetap boleh kosong. Itu jawaban tebakannya. Ada dua cara yang benar:

// up()
Schema::table('books', function (Blueprint $table) {
    $table->integer('year')->nullable();
});

// down()
Schema::table('books', function (Blueprint $table) {
    $table->dropColumn('year');
});

Schema::table mengubah tabel yang sudah ada, bukan membuat baru. Kolom barunya nullable(), karena buku yang sudah tersimpan belum punya tahun terbit. Di SQLite jadi alter table "books" add column "year" integer. Cara mengubah kolom di production tanpa bikin error dibahas di pelajaran Schema Migration Patterns.

nullable() dan default(): wajib atau boleh kosong

Coba simpan satu buku cuma dengan judul dan penulis (kode Eloquent ini dibahas di pelajaran berikutnya):

Book::create(['title' => 'Laskar Pelangi', 'author' => 'Andrea Hirata']);
Kolom isbn di migration Hasilnya
->nullable() masuk: stock 0 dari default(0), isbn NULL
tanpa ->nullable() ditolak: SQLSTATE[23000]: Integrity constraint violation: 19 NOT NULL constraint failed: books.isbn

Database yang menolaknya, bukan Laravel. Tiap kolom yang boleh gak diisi butuh ->nullable() atau ->default(...).

⚠️ Jebakan yang sering ketemu

TL;DR: Migration adalah file PHP berisi up() (perubahan) dan down() (kebalikannya), dibuat dengan php artisan make:migration create_books_table. Di dalam Schema::create, $table->id(), string() (teks maksimal 255), integer(), boolean(), dan timestamps() (dua kolom: created_at dan updated_at) menambah kolom, dan setiap kolom wajib diisi kecuali diberi ->nullable() atau ->default(...). php artisan migrate menjalankan file yang belum tercatat di tabel migrations dan memberinya nomor batch, migrate:status menampilkan Ran atau Pending, migrate:rollback membatalkan batch terakhir. File yang sudah tercatat gak pernah dijalankan lagi, jadi perubahan berikutnya ditulis di migration baru (Schema::table).

Yang akan kamu pelajari