Mengelola skema database sebagai kode: membuat migration dengan Database Forge, menjalankan php spark migrate, dan mengisi data awal dengan seeder untuk membangun skema blog yang konsisten di semua environment.

Setelah di episode 6 kita belajar berinteraksi dengan tabel posts lewat Query Builder dan Model, pertanyaan berikutnya adalah: bagaimana tabel itu dibuat? Kalau kalian menjalankan CREATE TABLE manual di MySQL, skema tidak tersimpan di git dan tiap developer (atau server produksi) harus membuatnya sendiri — rawan tidak konsisten.
Di episode 7 ini kita memecahkan masalah itu dengan migrations dan seeding: skema database dikelola sebagai kode PHP yang masuk git, bisa dijalankan berulang, dan diisi data awal secara terprogram. Ini bukan fitur opsional — ini standar tim profesional.
Migration adalah class PHP yang mendeskripsikan perubahan skema. Buat migration pertama:
php spark make:migration CreatePostsPerintah ini menghasilkan file di app/Database/Migrations/ dengan nama ber-timestamp seperti 2026-08-16-120000_CreatePosts.php. Buka dan isi dengan skema:
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreatePosts extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'title' => [
'type' => 'VARCHAR',
'constraint' => 255,
],
'slug' => [
'type' => 'VARCHAR',
'constraint' => 255,
],
'body' => [
'type' => 'TEXT',
],
'status' => [
'type' => 'VARCHAR',
'constraint' => 20,
'default' => 'draft',
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey('slug');
$this->forge->addKey('created_at');
$this->forge->createTable('posts');
}
public function down()
{
$this->forge->dropTable('posts');
}
}Database Forge ($this->forge) adalah API untuk memanipulasi struktur tabel. addField() mendefinisikan kolom, addKey('id', true) membuat primary key, dan createTable('posts') mengeksekusi pembuatan tabel. Method up() diterapkan saat migrate, down() membatalkannya — inilah mengapa migration bisa di-rollback dengan aman.
Note
addKey('slug') dan addKey('created_at') menambah index non-unique — penting untuk performa query WHERE slug = ... dan ORDER BY created_at. Ini contoh kecil dari optimasi yang akan kita dalami di episode 24.
php spark migrateOutput menampilkan migration yang berhasil dijalankan. Cek status:
php spark migrate:statusPerintah penting lainnya:
| Perintah | Fungsi |
|---|---|
php spark migrate | Terapkan migration yang belum jalan |
php spark migrate:rollback | Batalkan batch terakhir |
php spark migrate:refresh | Rollback semua lalu migrate ulang |
php spark migrate:status | Daftar migration dan statusnya |
php spark migrate --all | Jalankan di semua database group |
Migration hanya dijalankan sekali per file — status tersimpan di tabel migrations. Itulah mengapa skema bisa di-share antar developer dan environment tanpa duplikasi: git membawa file migration, setiap mesin menjalankannya sendiri.
Untuk series ini, kita butuh tiga tabel. Buat migration untuk users dan categories:
php spark make:migration CreateUsers
php spark make:migration CreateCategories<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateUsers extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'name' => [
'type' => 'VARCHAR',
'constraint' => 100,
],
'email' => [
'type' => 'VARCHAR',
'constraint' => 255,
],
'password' => [
'type' => 'VARCHAR',
'constraint' => 255,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey('email');
$this->forge->createTable('users');
}
public function down()
{
$this->forge->dropTable('users');
}
}Jalankan semua migration sekaligus:
php spark migrateWarning
Jangan pernah mengubah migration yang sudah pernah dijalankan di produksi — file migration adalah history, bukan "edit terus sampai benar". Kalau perlu perubahan skema, buat migration baru. Mengedit migration lama membuat database produksi kalian tidak sinkron dengan kode.
Migration membuat struktur; seeder mengisi data awal (user admin, kategori default, contoh post). Buat seeder:
php spark make:seeder AppSeeder<?php
namespace App\Database\Seeds;
use CodeIgniter\Database\Seeder;
class AppSeeder extends Seeder
{
public function run()
{
$this->db->table('users')->insert([
'name' => 'Admin',
'email' => 'admin@devvnull.test',
'password' => password_hash('rahasia123', PASSWORD_DEFAULT),
]);
$this->db->table('categories')->insertBatch([
['name' => 'Tutorial', 'slug' => 'tutorial'],
['name' => 'Berita', 'slug' => 'berita'],
['name' => 'Opini', 'slug' => 'opini'],
]);
echo "Seeding selesai\n";
}
}Jalankan seeder:
php spark db:seed AppSeederinsertBatch() mengisi banyak baris sekaligus dalam satu query. Perhatikan password_hash() — kita tidak pernah menyimpan password plaintext (akan dibahas di episode 12). spark db:seed tanpa argumen menjalankan seeder yang terdaftar di Config/Database.php $seeders; dengan argumen, menjalankan seeder spesifik.
Sekarang kalian punya workflow skema yang profesional:
git pull lalu php spark migrate — skema langsung sama.up() dan down().addField(), addKey(), createTable().php spark migrate / rollback / refresh / status mengelola siklus skema.insertBatch() atau insert().Inti yang harus dibawa pulang:
addKey) untuk kolom yang sering di-query.Di episode 8 selanjutnya kita akan membahas form validation dan security — validation rules, custom rules, error handling, serta perlindungan CSRF, XSS filtering, dan input escaping. Ini episode pertama dari tema keamanan yang akan menemani kalian sampai episode 20!