Docs LATEST

Migrations

Database migrations in PHP and SQL formats.

Database & Migrations

Migrations let you version-control your database schema. VelvetCMS supports both PHP migrations (using the Schema Builder) and raw SQL migrations.

Migration Location #

Migrations live in database/migrations/. Modules can also include their own migrations.

Running Migrations #

./velvet migrate

This runs all pending migrations in filename order.

PHP Migrations #

PHP migrations use the Schema Builder for database-agnostic schema changes:

<?php
// database/migrations/001_create_posts_table.php

use VelvetCMS\Database\Schema\Schema;
use VelvetCMS\Database\Schema\Blueprint;

return new class {
    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->string('slug')->unique();
            $table->text('content');
            $table->string('status', 20)->default('draft');
            $table->timestamps();
            
            $table->index('status');
        });
    }
};

The migration file must return an object with an up() method.

SQL Migrations #

For raw SQL, create .sql or .up.sql files:

-- database/migrations/002_add_published_at.up.sql

ALTER TABLE posts ADD COLUMN published_at TIMESTAMP NULL;
CREATE INDEX idx_posts_published_at ON posts(published_at);

Driver-Specific SQL #

Different databases have different SQL dialects. You can provide driver-specific versions:

003_create_fulltext_index.sqlite.up.sql
003_create_fulltext_index.mysql.up.sql
003_create_fulltext_index.pgsql.up.sql

The migrator picks the most specific file for your current driver:

  1. {name}.{driver}.up.sql (e.g., 003_create_index.mysql.up.sql)
  2. {name}.up.sql (generic SQL)
  3. {name}.php (PHP migration)

Naming Convention #

Migrations run in filename order. Use numeric prefixes to control the sequence:

001_create_users_table.php
002_create_posts_table.php
003_add_author_to_posts.php
010_create_comments_table.php

Leave gaps in numbering to allow for future insertions.

Migration Tracking #

VelvetCMS tracks which migrations have run in a migrations table. Each migration runs exactly once, even if you call migrate multiple times.

Batch Tracking #

Migrations run together are grouped into a batch. This is useful for understanding when migrations were applied.

Creating Tables #

Schema::create('users', function (Blueprint $table) {
    $table->id();
    $table->string('email')->unique();
    $table->string('password');
    $table->boolean('active')->default(true);
    $table->timestamps();
});

Dropping Tables #

Schema::drop('old_table');

// Only if exists (no error if missing)
Schema::dropIfExists('old_table');

Module Migrations #

Modules can include their own migrations in database/migrations/. When the module is loaded, its migrations are discovered and run with the core migrations.

Tips #

  • Keep migrations small - one logical change per migration
  • Never edit a migration that's been run - create a new one instead
  • Test migrations on a copy - especially for production databases
  • Use PHP migrations for portability - they work across all supported databases