Day 4 · Database & Migration

Day 4 — Database & Migration

Setelah membangun halaman dengan routing, Blade, dan component, sekarang kita menyiapkan tempat penyimpanan data yang rapi. Kita akan menghubungkan Laravel ke database dan mencatat perubahan struktur tabel melalui migration.

⏱️ 3–4 jam🗄️ Struktur database🌱 Ramah pemula🎯 Hasil: tabel siswa

Target Belajar Hari Ini

Pada Day 1–3, data contoh masih bisa ditulis langsung di route atau view. Cara itu cukup untuk belajar tampilan, tetapi aplikasi sekolah yang nyata perlu menyimpan data siswa secara tetap. Hari ini kita menyiapkan fondasinya: database dan struktur tabel.

Migration bukan alat untuk mengisi atau mengambil data siswa. Migration mengatur bentuk tempat penyimpanannya: tabel apa yang tersedia, nama kolomnya, tipe datanya, dan hubungan dasarnya. Cara mengambil data sebagai object Laravel baru akan dibahas pada Day 5 melalui Eloquent ORM.

Setelah menyelesaikan enam bagian awal ini, kamu ditargetkan mampu:

  • Memahami mengapa migration lebih aman daripada membuat tabel manual lewat phpMyAdmin.
  • Mengatur koneksi database Laravel melalui file .env.
  • Membuat migration sendiri dan memahami pasangan method up() serta down().
  • Memilih tipe kolom dasar yang sesuai untuk data sekolah.
  • Menjalankan migration, memeriksa status, dan melakukan rollback dengan aman.

Empat perintah berikut akan menjadi peta perjalanan kita. Tidak perlu menghafalnya sekaligus; pahami tugas setiap perintah dan biasakan memeriksa hasilnya.

Terminal — peta perintah Day 4
# Membuat file rencana struktur tabel
php artisan make:migration create_siswa_table

# Menjalankan rencana yang belum pernah dijalankan
php artisan migrate

# Melihat riwayat/status migration
php artisan migrate:status

# Membatalkan satu batch terakhir
php artisan migrate:rollback
⚠️ Kesalahan umum: langsung mengejar tabel tanpa memahami alurJangan sekadar menyalin perintah sampai tabel muncul. Sebelum menekan Enter, sebutkan dulu apakah perintah itu membuat file migration, mengubah database, atau membatalkan perubahan. Kebiasaan ini mencegah salah perintah.
📌 Kapan memakai materi ini?Pakai migration ketika struktur database merupakan bagian aplikasi yang perlu dibagikan dan dipasang ulang. Jangan gunakan migration untuk perubahan data harian seperti mengganti alamat seorang siswa; itu tugas query, form, atau Eloquent pada materi berikutnya.

Kenapa Laravel Pakai Migration (bukan bikin tabel manual lewat phpMyAdmin)

Migration adalah riwayat perubahan struktur database yang ditulis sebagai kode. Anggap seperti buku renovasi gedung sekolah: setiap penambahan ruang, penggantian nama ruang, dan pembongkaran dicatat berurutan. Orang lain dapat membaca catatan yang sama lalu membangun susunan gedung yang sama.

Jika tabel diubah manual lewat phpMyAdmin, database di laptop kita memang berubah, tetapi perubahan itu tidak otomatis tercatat di proyek Git. Akibatnya, anggota tim dan server tidak tahu tombol apa yang tadi diklik, kolom apa yang ditambah, atau tipe data apa yang dipilih.

Contoh nyata: Developer A menambah kolom nis di laptopnya lewat phpMyAdmin. Kodenya berjalan di laptop A, tetapi Developer B menarik source code yang sama tanpa menerima perubahan database. Saat aplikasi dipasang di server production, halaman gagal karena kolom nis tidak ada.

Dengan migration, Developer A membuat file dan membagikannya bersama kode. Developer B serta server cukup menjalankan php artisan migrate. Laravel melihat migration mana yang belum dijalankan, mengeksekusinya secara urut, lalu mencatat hasilnya di tabel internal bernama migrations.

Alur kerja tim yang bisa diulang
# Developer A: buat perubahan lalu commit file migration
php artisan make:migration add_nis_to_siswa_table

git add database/migrations
git commit -m "Tambah kolom NIS siswa"

# Developer B / server: ambil kode lalu jalankan perubahan
git pull
php artisan migrate
Gambaran isi riwayat migration
database/migrations/
├── 2026_08_23_080000_create_siswa_table.php
└── 2026_08_23_093000_add_nis_to_siswa_table.php

# Urutan timestamp membuat perubahan dapat dijalankan berurutan.
⚠️ Kesalahan umum: migration dibuat, tetapi file tidak ikut GitPerubahan akan tetap hanya ada di laptop sendiri jika file di database/migrations tidak di-commit. Perbaiki dengan memeriksa git status, commit file migration, lalu minta rekan menjalankan php artisan migrate.
📌 Migration vs phpMyAdminPakai migration untuk perubahan struktur aplikasi: membuat tabel, menambah kolom, index, dan foreign key. phpMyAdmin tetap berguna untuk melihat data atau inspeksi cepat, tetapi jangan menjadikannya sumber utama perubahan struktur yang harus konsisten di tim dan server.

Setup Koneksi Database (.env, config/database.php)

Sebelum Laravel dapat membuat tabel, aplikasi harus mengetahui mesin database, alamat server, nama database, serta akun yang dipakai. Nilai yang berbeda di setiap komputer disimpan di .env, sehingga konfigurasi lokal tidak perlu ditulis permanen ke source code.

Buat database kosong terlebih dahulu, misalnya sekolah_laravel. Database kosong ini seperti lemari yang sudah tersedia; migration nanti akan membuat rak-rak atau tabel di dalamnya. Contoh berikut memakai MySQL yang berjalan di komputer yang sama.

.env — contoh koneksi MySQL lokal
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=sekolah_laravel
DB_USERNAME=root
DB_PASSWORD=rahasia

File config/database.php menyediakan konfigurasi untuk MySQL, PostgreSQL, SQLite, dan mesin lain. Pada bagian MySQL, fungsi env() membaca nilai dari .env. Biasanya kita tidak perlu mengubah file config ini; cukup mengubah nilai lingkungan.

config/database.php — potongan yang membaca .env
'default' => env('DB_CONNECTION', 'sqlite'),

'connections' => [
    'mysql' => [
        'driver'   => 'mysql',
        'host'     => env('DB_HOST', '127.0.0.1'),
        'port'     => env('DB_PORT', '3306'),
        'database' => env('DB_DATABASE'),
        'username' => env('DB_USERNAME'),
        'password' => env('DB_PASSWORD'),
    ],
],

Setelah mengubah .env, bersihkan cache konfigurasi agar proses Laravel berikutnya membaca nilai terbaru. Lalu uji koneksi melalui status migration. Jika status dapat dibaca tanpa pesan koneksi gagal, Laravel sudah dapat berbicara dengan database.

Terminal — muat ulang konfigurasi dan uji koneksi
php artisan config:clear
php artisan migrate:status
⚠️ Kesalahan umum: nama database/password salah atau konfigurasi lama masih tersimpanPeriksa bahwa database benar-benar sudah dibuat dan kredensial cocok. Sesudah mengubah .env, jalankan php artisan config:clear. Jika memakai php artisan serve atau worker yang berjalan lama, hentikan lalu jalankan kembali agar proses memuat lingkungan terbaru.
📌 .env vs config/database.phpUbah .env untuk nilai khusus komputer/server seperti password, host, dan nama database. Ubah config/database.php hanya ketika perlu aturan konfigurasi aplikasi yang memang harus dibagikan. Jangan commit file .env karena dapat berisi rahasia.

Membuat Migration (php artisan make:migration, struktur file up()/down())

Artisan dapat membuat kerangka migration agar nama class, import, dan format file konsisten. Nama create_siswa_table menyatakan niat dengan jelas: migration ini bertugas membuat tabel siswa.

Terminal — membuat migration tabel siswa
php artisan make:migration create_siswa_table

# Contoh hasil:
# database/migrations/2026_08_23_101530_create_siswa_table.php

Timestamp pada awal nama file bukan hiasan. Laravel memakainya untuk mengurutkan migration. Dua developer dapat membuat migration dengan nama berbeda, dan urutan waktu membantu Laravel menentukan perubahan mana yang dijalankan lebih dahulu.

File migration mengembalikan class anonim yang memiliki dua method. up() menjelaskan langkah maju—dalam contoh ini membuat tabel. down() menjelaskan kebalikannya—menghapus tabel tersebut ketika migration di-rollback.

database/migrations/..._create_siswa_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('siswa', function (Blueprint $table) {
            $table->id();
            $table->string('nama');
            $table->timestamps();
        });
    }

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

Pikirkan up() dan down() sebagai tombol pasang dan bongkar. Jika up() membuat tabel, down() menghapus tabel. Jika up() menambah satu kolom pada tabel lama, down() semestinya menghapus kolom yang sama—bukan menghapus seluruh tabel.

Contoh pasangan perubahan yang seimbang
public function up(): void
{
    Schema::table('siswa', function (Blueprint $table) {
        $table->string('nis')->unique();
    });
}

public function down(): void
{
    Schema::table('siswa', function (Blueprint $table) {
        $table->dropColumn('nis');
    });
}
⚠️ Kesalahan umum: file dibuat tetapi up() kosong, atau down() tidak membalik up()Isi perubahan di up() sebelum menjalankan migrate. Setelah itu baca pasangan down() seperti kalimat kebalikan. Jangan sampai rollback penambahan satu kolom justru menghapus seluruh tabel dan datanya.
📌 Buat migration baru atau edit yang lama?Ketika migration belum pernah dibagikan dan hanya dipakai lokal, mengeditnya masih wajar. Jika migration sudah dijalankan oleh tim atau production, jangan mengubah sejarah diam-diam; buat migration baru seperti add_nis_to_siswa_table.

Schema Builder — Kolom & Tipe Data Umum (string, integer, boolean, timestamp, foreign key dasar)

Schema Builder adalah kosakata Laravel untuk menjelaskan struktur database dengan PHP. Object $table adalah rancangan tabel, sedangkan method seperti string() atau integer() memilih tipe kolom. Laravel kemudian menerjemahkannya ke SQL sesuai mesin database.

Pilih tipe berdasarkan makna data, bukan hanya bentuk tampilannya. Nama dan NIS cocok sebagai string; NIS bukan angka untuk dihitung dan bisa memiliki nol di depan. Jumlah poin cocok sebagai integer, sedangkan status aktif cocok sebagai boolean.

Migration lengkap — tabel siswa
Schema::create('siswa', function (Blueprint $table) {
    $table->id();
    $table->string('nama', 150);
    $table->string('nis', 20)->unique();
    $table->string('kelas', 20);
    $table->integer('poin')->default(0);
    $table->boolean('aktif')->default(true);
    $table->timestamp('tanggal_lulus')->nullable();
    $table->timestamps();
});

timestamps() adalah pintasan yang membuat dua kolom: created_at dan updated_at. Berbeda dengan itu, timestamp('tanggal_lulus') membuat satu kolom waktu dengan nama yang kita tentukan. Karena siswa aktif belum tentu lulus, kolom tersebut diberi nullable() agar boleh kosong.

Foreign key menghubungkan satu tabel dengan tabel lain. Misalnya setiap siswa berada di satu kelas. foreignId('kelas_id')->constrained('kelas') membuat kolom angka kelas_id dan aturan bahwa nilainya harus menunjuk ke id yang benar-benar ada pada tabel kelas.

Foreign key dasar — tabel kelas dibuat lebih dahulu
Schema::create('kelas', function (Blueprint $table) {
    $table->id();
    $table->string('nama');
    $table->timestamps();
});

Schema::create('siswa', function (Blueprint $table) {
    $table->id();
    $table->string('nama');
    $table->foreignId('kelas_id')->constrained('kelas');
    $table->timestamps();
});

Urutan migration penting untuk foreign key: tabel tujuan kelas harus sudah ada sebelum tabel siswa mencoba merujuknya. Timestamp nama file membantu memastikan migration pembuat tabel kelas berjalan lebih dahulu.

⚠️ Kesalahan umum: kolom opsional tidak nullable atau tipe datanya tidak sesuaiTanpa nullable(), database menolak penyimpanan saat nilai belum tersedia. Jangan memakai integer untuk NIS/nomor telepon karena nol di depan dapat hilang dan datanya tidak dihitung. Tentukan contoh nilai nyata sebelum memilih tipe.
📌 Kapan memakai foreign key?Pakai foreign key ketika data harus menunjuk record lain yang sah, seperti siswa ke kelas. Tidak perlu membuat foreign key untuk label sederhana yang berdiri sendiri. Relasi dalam model Eloquent akan dibahas Day 5; hari ini kita baru menjaga struktur databasenya.

Menjalankan Migration (migrate, migrate:rollback, migrate:fresh — beda dan kapan pakai)

Setelah file migration siap dan koneksi benar, php artisan migrate menjalankan hanya migration yang statusnya masih Pending. Laravel tidak mengulang semua file setiap kali; file yang berhasil dicatat beserta nomor batch pada tabel migrations.

Terminal — periksa lalu jalankan
# Lihat mana yang sudah Ran dan mana yang Pending
php artisan migrate:status

# Jalankan seluruh migration yang masih Pending
php artisan migrate

migrate:rollback membatalkan satu batch terakhir, bukan selalu satu file. Jika tiga migration dijalankan bersama dalam satu perintah migrate, ketiganya bisa berada di batch yang sama dan akan dipanggil method down()-nya saat rollback.

Terminal — membatalkan perubahan terakhir
php artisan migrate:rollback

# Opsional: batalkan jumlah migration tertentu dari belakang
php artisan migrate:rollback --step=1

# Cek kembali hasilnya
php artisan migrate:status

migrate:fresh berbeda jauh: perintah ini menghapus semua tabel pada koneksi database, lalu menjalankan semua migration dari awal. Ia tidak hanya membatalkan batch terakhir. Perintah ini nyaman saat latihan lokal dan struktur sedang sering dirombak, tetapi berbahaya untuk database yang berisi data penting.

Terminal — mulai ulang database lokal dari nol
# HANYA jika seluruh data boleh dihapus
php artisan migrate:fresh

# Nanti, jika seeder sudah tersedia:
php artisan migrate:fresh --seed

Ringkasnya: gunakan migrate untuk maju secara normal, migrate:status untuk memeriksa keadaan, migrate:rollback untuk menguji atau membatalkan batch terbaru, dan migrate:fresh untuk mereset lingkungan latihan. Selalu pastikan terminal sedang berada di proyek dan database yang benar.

🚨 HATI-HATI: migrate:fresh menghapus semua tabel dan dataJangan menjalankannya di production atau database bersama. Sebelum Enter, baca DB_HOST dan DB_DATABASE aktif, pastikan ada backup jika datanya penting, dan pilih migrate biasa jika hanya ingin memasang perubahan baru.
⚠️ Kesalahan umum: mengira rollback membatalkan satu file sajaRollback bekerja berdasarkan batch. Jalankan php artisan migrate:status, pahami file yang masuk batch terakhir, dan pastikan setiap down() benar sebelum melakukan rollback.
📌 Perintah mana yang dipakai?migrate: pengembangan normal dan deployment. status: sebelum/sesudah perubahan atau saat debugging. rollback: menguji pasangan up/down atau membatalkan batch terakhir. fresh: hanya database lokal/testing yang seluruh datanya boleh hilang.

Mengelola Migration (case-study — kasus nyata)

Membuat migration pertama relatif mudah. Tantangan sebenarnya muncul ketika database sudah dipakai, anggota tim bertambah, dan data tidak boleh dihapus. Prinsip utamanya: migration yang sudah dibagikan adalah riwayat. Jangan menulis ulang masa lalu; tambahkan langkah perubahan baru yang bisa dijalankan semua lingkungan.

Kasus 1 — Production perlu kolom baru

Masalah: migration pembuat tabel siswa sudah dijalankan di production, tetapi sekarang sekolah membutuhkan kolom tanggal_lahir. Mengedit file create_siswa_table lama tidak mengubah database production karena Laravel sudah mencatat file itu sebagai Ran.

Solusi: buat migration baru yang namanya menjelaskan perubahan. Method up() menambah kolom; down() menghapus kolom itu jika batch perlu dibatalkan.

Terminal
php artisan make:migration add_tanggal_lahir_to_siswa_table --table=siswa
Migration baru — tambah kolom tanpa mengganggu data lama
public function up(): void
{
    Schema::table('siswa', function (Blueprint $table) {
        $table->date('tanggal_lahir')->nullable();
    });
}

public function down(): void
{
    Schema::table('siswa', function (Blueprint $table) {
        $table->dropColumn('tanggal_lahir');
    });
}

nullable() penting karena record siswa lama belum memiliki tanggal lahir. Setelah migration baru di-review, backup database production, jalankan php artisan migrate, lalu isi data secara bertahap. Pola nama yang sama berlaku untuk kebutuhan lain, misalnya add_alamat_to_siswa_table.

Kasus 2 — Salah nama atau tipe kolom setelah migrate

Masalah: kolom sudah bernama nam padahal seharusnya nama, atau nis telanjur berupa integer. Kita tidak boleh memakai migrate:fresh karena data lain harus tetap ada.

Solusi: buat migration koreksi baru. Untuk rename, gunakan renameColumn(). Untuk perubahan tipe, gunakan change() dan pastikan perubahan tersebut didukung versi Laravel serta driver database proyek.

Terminal dan isi migration koreksi
php artisan make:migration fix_columns_on_siswa_table --table=siswa

Schema::table('siswa', function (Blueprint $table) {
    $table->renameColumn('nam', 'nama');
    $table->string('nis', 20)->change();
});

Pada Laravel/DBAL versi lama, perubahan kolom tertentu memerlukan paket doctrine/dbal melalui Composer. Pada versi modern banyak operasi sudah ditangani native. Baca dokumentasi versi proyek dan uji pada salinan database. Saat mengubah tipe, audit datanya dahulu—teks yang tidak bisa dikonversi dapat membuat migration gagal atau kehilangan arti.

⚠️ Method down() harus benar-benar membalikkan perubahanUntuk rename, balikkan nama menjadi nam. Untuk perubahan tipe, tentukan tipe sebelumnya secara eksplisit. Jangan menganggap rollback selalu aman tanpa mengujinya di lokal/staging.

Kasus 3 — migrate:fresh tidak sengaja dijalankan di production

Masalah: perintah ini menjatuhkan seluruh tabel lalu membuatnya dari awal. Semua data siswa, kelas, nilai, akun, dan tabel internal pada koneksi aktif dapat hilang. Migration hanya membangun kembali struktur; ia tidak mengembalikan data nyata.

Solusi setelah kejadian: hentikan aplikasi atau aktifkan maintenance mode agar tidak ada data baru masuk, jangan melakukan eksperimen lanjutan, beri tahu penanggung jawab, lalu pulihkan database dari backup terakhir yang tervalidasi. Jika tidak ada backup, migration tidak dapat menyulap data lama kembali.

🚨 Pencegahan wajibJangan pernah menjalankan migrate:fresh di production. Sebelum migration apa pun di server nyata, verifikasi environment dan nama database, buat backup, pastikan backup bisa dipulihkan, review migration, lalu gunakan php artisan migrate --force hanya dalam prosedur deployment yang terkontrol.

Kasus 4 — Foreign key gagal karena urutan migration

Masalah: migration siswa berjalan lebih dulu dan mencoba membuat kelas_id yang mengacu ke tabel kelas, padahal tabel kelas belum ada. Database tidak dapat membuat aturan referensi ke target yang belum tersedia.

Solusi: migration tabel yang direferensikan (kelas) harus memiliki timestamp nama file lebih awal daripada migration tabel yang mereferensikan (siswa). Saat menghapus, lakukan kebalikannya: hapus siswa sebelum kelas.

Urutan file yang benar
2026_08_23_080000_create_kelas_table.php
2026_08_23_081000_create_siswa_table.php

// di create_siswa_table:
$table->foreignId('kelas_id')->constrained('kelas');

Jika file masih baru dan belum dibagikan, perbaiki timestamp lalu uji dari database lokal kosong. Jika sudah dipakai tim/production, jangan mengganti riwayat sembarangan; buat migration koreksi yang menambahkan constraint setelah kedua tabel tersedia.

Kasus 5 — Migration sudah di Git, tetapi komputer lain tertinggal

Masalah: rekan menarik kode terbaru, tetapi aplikasinya error “unknown column” atau “table not found”. Git menyinkronkan file migration, bukan otomatis menjalankan perubahan ke database lokal mereka.

Solusi: setelah git pull atau deployment, jalankan status lalu migration pending pada komputer/server tersebut.

Sinkronisasi struktur database
git pull
php artisan migrate:status
php artisan migrate

Laravel menjalankan hanya file berstatus Pending. Jangan mengirim dump database hanya untuk menyamakan struktur. Di server production, migration sebaiknya menjadi tahap deployment dengan backup, review, dan log yang jelas.

Kasus 6 — Migration atau edit langsung lewat database client?

Masalah: phpMyAdmin, TablePlus, atau DBeaver terasa lebih cepat untuk menambah kolom. Namun perubahan itu hanya terjadi pada satu database, tidak tercatat di Git, tidak memiliki rollback, dan mudah membuat laptop anggota tim berbeda dengan server.

Solusi: gunakan migration untuk setiap perubahan struktur permanen yang perlu direkam, di-review, dan dibagikan: membuat/menghapus tabel, menambah/mengubah kolom, index, unique constraint, atau foreign key. Edit struktur langsung lewat client hanya boleh sebagai eksperimen/debugging sementara di database lokal yang boleh dibuang; setelah memahami solusinya, kembalikan eksperimen dan tulis migration resmi.

SituasiPilihanAlasan
Tambah kolom alamat untuk fitur baruMigration baruPerubahan permanen dan harus sampai ke semua environment.
Tambah unique index pada NISMigration baruAturan data harus tercatat dan dapat direview.
Mencoba tipe kolom pada database lokal sekali pakaiDatabase client sementaraBoleh untuk debugging, tetapi hasil akhirnya tetap migration.
Mengubah struktur production lewat phpMyAdminJanganMenyebabkan schema drift dan tidak punya riwayat yang dapat diulang.
✅ Mental model yang amanGit membawa file migration; php artisan migrate menerapkan file yang belum dijalankan; backup melindungi data. Ketiganya memiliki fungsi berbeda dan semuanya dibutuhkan dalam perubahan production.

Praktik Mini — Tabel Kelas & Siswa Berelasi

Kita akan membuat dua tabel: kelas sebagai tabel induk dan siswa sebagai tabel anak. Setiap siswa wajib menunjuk satu kelas melalui kelas_id. Karena foreign key hanya boleh menunjuk tabel yang sudah ada, buat migration kelas lebih dahulu.

Langkah 1 — Buat migration sesuai urutan

Terminal — jalankan berurutan
php artisan make:migration create_kelas_table
php artisan make:migration create_siswa_table

Periksa timestamp kedua nama file di database/migrations. Timestamp create_kelas_table harus lebih kecil/awal. Jika keduanya dibuat pada detik yang sama dan urutannya keliru, ubah timestamp sebelum migration pernah dibagikan atau dijalankan.

Langkah 2 — Lengkapi migration tabel kelas

database/migrations/..._create_kelas_table.php
public function up(): void
{
    Schema::create('kelas', function (Blueprint $table) {
        $table->id();
        $table->string('nama_kelas', 30)->unique();
        $table->string('wali_kelas', 150);
        $table->timestamps();
    });
}

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

unique() mencegah dua record memakai nama kelas yang sama. Dalam aplikasi nyata, aturan ini disesuaikan kebutuhan—misalnya nama kelas dapat berulang pada tahun ajaran berbeda sehingga diperlukan kombinasi kolom.

Langkah 3 — Lengkapi migration tabel siswa

database/migrations/..._create_siswa_table.php
public function up(): void
{
    Schema::create('siswa', function (Blueprint $table) {
        $table->id();
        $table->string('nama', 150);
        $table->string('nis', 20)->unique();
        $table->foreignId('kelas_id')
              ->constrained('kelas')
              ->restrictOnDelete();
        $table->timestamps();
    });
}

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

constrained('kelas') menghubungkan siswa.kelas_id ke kelas.id. restrictOnDelete() mencegah kelas dihapus selama masih memiliki siswa, sehingga data tidak menjadi yatim. Ini pilihan aman untuk latihan; kebijakan penghapusan harus selalu mengikuti aturan bisnis.

Langkah 4 — Periksa, jalankan, dan verifikasi

Terminal
php artisan migrate:status
php artisan migrate
php artisan migrate:status

Pastikan kedua migration berstatus Ran. Periksa database client bila perlu: tabel kelas memiliki kolom id, nama_kelas, wali_kelas, dan timestamps; tabel siswa memiliki kelas_id beserta foreign key ke kelas.id.

Langkah 5 — Uji jalur balik di database lokal

Terminal — hanya database latihan
php artisan migrate:rollback
php artisan migrate:status
php artisan migrate

Karena kedua migration kemungkinan berada pada batch yang sama, rollback menjalankan down() dari urutan terakhir: tabel siswa dilepas sebelum kelas. Jalankan migrate lagi agar latihan berakhir dengan kedua tabel tersedia.

✅ Hasil praktikKamu sekarang memiliki struktur relasional yang konsisten: satu kelas dapat memiliki banyak siswa, sementara setiap record siswa hanya dapat menunjuk kelas yang benar-benar ada.

Tugas / PR — Uji Pemahaman Hari Ini

Studi kasus: sekolah membutuhkan tabel baru nilai. Setiap nilai dimiliki satu siswa dan mencatat mata_pelajaran, angka nilai, serta semester. Kerjakan bertingkat pada branch terpisah dan jangan menyalin solusi lengkap dari luar.

Soal A — Dasar: rancang tabel nilai

Buat migration create_nilai_table dengan primary key, kolom mata_pelajaran, nilai, semester, dan timestamps. Pilih tipe data serta batas panjang yang masuk akal, lalu jelaskan pilihanmu pada deskripsi PR.

Kriteria selesai:

  • Migration dapat dijalankan tanpa error pada database lokal.
  • Method up() membuat tabel dan down() menghapus tabel yang sama.
  • Tipe kolom tidak dipilih asal-asalan; nilai numerik dan semester memiliki bentuk yang konsisten.
💡 HintUntuk nilai bulat 0–100, unsignedTinyInteger dapat menjadi titik awal diskusi.

Soal B — Menengah: hubungkan ke siswa dan jaga datanya

Tambahkan siswa_id sebagai foreign key ke siswa.id. Tentukan kebijakan ketika siswa akan dihapus, lalu buat constraint agar siswa tidak memiliki dua nilai untuk mata pelajaran dan semester yang sama.

Kriteria selesai:

  • Migration nilai berjalan setelah migration siswa.
  • Foreign key benar-benar mengarah ke siswa.id, bukan hanya kolom integer biasa.
  • Ada composite unique constraint untuk kombinasi data yang disepakati.
  • Kebijakan delete (restrict atau cascade) dijelaskan beserta risikonya di PR.
💡 HintConstraint gabungan dapat dibuat dengan $table->unique([...]).

Soal C — Tantangan terbuka: perubahan setelah production

Bayangkan migration nilai sudah berjalan dan menyimpan ribuan record di production. Sekolah kemudian meminta kolom catatan_guru yang opsional, dan semester ingin diubah ke format baru. Susun rencana perubahan aman—file apa yang dibuat, urutan deployment, perlakuan terhadap data lama, backup, pengujian, dan rencana rollback. Implementasikan hanya pada database lokal/staging.

Kriteria selesai:

  • Tidak mengedit migration lama dan tidak memakai migrate:fresh.
  • Rencana menggunakan migration baru dan mempertimbangkan kompatibilitas data lama.
  • Ada langkah backup, audit/transformasi data, uji staging, deployment, serta verifikasi sesudah migrate.
  • PR menyertakan output migrate:status dan bukti rollback diuji pada data contoh.
💡 HintPerubahan nullable dapat dirilis lebih dahulu sebelum aplikasi mulai mengisinya.
⚠️ Batas tugasJangan menempel kode solusi lengkap. Yang dinilai adalah rancangan schema, constraint yang melindungi data, urutan migration, dan alasan keputusanmu.

Playground PHP — Simulasi Struktur Migration

Migration asli membutuhkan Laravel dan database, sedangkan playground ini menjalankan PHP murni di browser memakai WebAssembly (php-wasm). Array asosiatif di bawah mensimulasikan rancangan tabel: nama kolom menjadi key dan tipe/constraint menjadi value. Ini membantu membaca schema tanpa mengubah database sungguhan.

🐘 PHP PlaygroundMemuat mesin PHP…
Hasil
Mesin PHP sedang disiapkan…
Tip: tekan Ctrl + Enter untuk menjalankan. Proses pertama mungkin butuh beberapa detik.
🔄 Hubungannya dengan migration LaravelArray ini hanya model mental, bukan pengganti Schema Builder. Pada migration asli, key seperti nama ditulis sebagai method $table->string('nama'), dan Laravel menerjemahkannya menjadi struktur database.

Latihan Mandiri & Checklist Selesai

Latihan mandiri

  1. Di playground, tambahkan tabel nilai dengan empat kolom yang diminta pada tugas.
  2. Pilih contoh tambah kolom dan ubah nama kolom baru; amati bahwa struktur lama tetap dipertahankan.
  3. Di proyek Laravel lokal, jalankan praktik kelas–siswa dari migrate, rollback, sampai migrate kembali.
  4. Buat satu migration baru untuk menambah kolom opsional pada tabel siswa tanpa mengedit migration lama.
  5. Jelaskan dengan kata sendiri mengapa tabel induk harus dibuat lebih dahulu daripada foreign key.

Checklist sebelum lanjut Day 5

  • Bisa menjelaskan fungsi migration, up(), down(), dan tabel migrations.
  • Bisa memilih tipe kolom dasar, nullable, unique constraint, dan foreign key sesuai kebutuhan.
  • Bisa menjalankan migrate, status, dan rollback dengan aman.
  • Tahu bahwa perubahan schema production memakai migration baru—bukan edit file lama atau phpMyAdmin.
  • Bisa menyusun migration tabel induk dan anak dalam urutan yang benar.
  • Tidak akan memakai migrate:fresh pada production dan selalu memikirkan backup.

🏁 Milestone Day 4 Tercapai!

Hari ini kamu sudah memahami migration, mampu membangun serta mengubah struktur tabel lewat kode, dan dapat menjaga relasi database dengan foreign key. Fondasi databasenya siap—besok kita lanjut memakai Eloquent ORM untuk membaca dan mengelola datanya melalui PHP.