Docs LATEST

Schema Builder

Database-agnostic schema definitions with Blueprint.

Database & Migrations

The Schema Builder provides a fluent, database-agnostic way to define tables. It generates the appropriate SQL for SQLite, MySQL, and PostgreSQL automatically.

Creating Tables #

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

Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->string('title');
    $table->string('slug', 100);
    $table->text('content');
    $table->timestamps();
});

Dropping Tables #

Schema::drop('posts');

// Only if it exists
Schema::dropIfExists('posts');

Column Types #

Primary Keys #

$table->id();                    // BIGINT auto-increment primary key named 'id'
$table->id('custom_id');         // Custom column name

Strings #

$table->string('name');           // VARCHAR(255)
$table->string('code', 50);       // VARCHAR(50)
$table->text('description');      // TEXT
$table->longText('body');          // LONGTEXT (MySQL) / TEXT (PostgreSQL, SQLite)

Numbers #

$table->integer('count');                          // INTEGER
$table->integer('count', true);                    // INTEGER AUTO_INCREMENT
$table->integer('count', false, true);             // INTEGER UNSIGNED
$table->bigInteger('views');                       // BIGINT
$table->bigInteger('id', true, true);              // BIGINT UNSIGNED AUTO_INCREMENT

Booleans #

$table->boolean('active');        // BOOLEAN (or TINYINT(1) on MySQL)

JSON #

$table->json('metadata');         // JSON (MySQL) / JSONB (PostgreSQL) / TEXT (SQLite)

Timestamps #

$table->timestamp('published_at');    // TIMESTAMP
$table->timestamps();                  // created_at and updated_at, both nullable

Column Modifiers #

Chain modifiers after the column definition:

$table->string('nickname')->nullable();           // Allow NULL
$table->string('status')->default('draft');       // Default value
$table->integer('views')->unsigned();             // Unsigned integer
$table->timestamp('created_at')->useCurrent();    // DEFAULT CURRENT_TIMESTAMP
$table->string('email')->nullable()->default(null);

Indexes #

Regular Index #

// On last defined column
$table->string('email')->index();

// On specific column(s)
$table->index('email');
$table->index(['first_name', 'last_name']);

// With custom name
$table->index('email', 'idx_users_email');

Unique Index #

$table->string('email')->unique();
$table->unique('slug');
$table->unique(['tenant_id', 'slug']);   // Composite unique

Primary Key #

$table->primary('id');
$table->primary(['tenant_id', 'id']);   // Composite primary key

Foreign Keys #

$table->bigInteger('user_id', false, true);  // Unsigned bigint
$table->foreign('user_id')
    ->references('id')
    ->on('users')
    ->onDelete('cascade');

Full foreign key example:

Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->bigInteger('author_id', false, true);
    $table->string('title');
    $table->text('content');
    $table->timestamps();
    
    $table->foreign('author_id')
        ->references('id')
        ->on('users')
        ->onDelete('cascade')
        ->onUpdate('cascade');
});

Complete Example #

Schema::create('comments', function (Blueprint $table) {
    $table->id();
    $table->bigInteger('post_id', false, true);
    $table->bigInteger('user_id', false, true)->nullable();
    $table->string('author_name')->nullable();
    $table->string('author_email')->nullable();
    $table->text('body');
    $table->string('status', 20)->default('pending');
    $table->timestamps();
    
    $table->index('status');
    $table->index(['post_id', 'status']);
    
    $table->foreign('post_id')
        ->references('id')
        ->on('posts')
        ->onDelete('cascade');
        
    $table->foreign('user_id')
        ->references('id')
        ->on('users')
        ->onDelete('set null');
});

Driver Differences #

The Schema Builder handles dialect differences automatically:

Feature SQLite MySQL PostgreSQL
Auto-increment INTEGER PRIMARY KEY AUTO_INCREMENT SERIAL
Boolean INTEGER TINYINT(1) BOOLEAN
Unsigned Ignored UNSIGNED Ignored
longText TEXT LONGTEXT TEXT
JSON TEXT JSON JSONB

If you need truly driver-specific DDL, use raw SQL migrations instead.