Skip to content

Schema Builder ​

The AvelPress Schema Builder provides a database-agnostic way to create and modify database tables. It offers a fluent interface for defining table structures, making it easy to manage your database schema.

Introduction ​

The Schema Builder works by defining blueprints for your tables. These blueprints contain column definitions, indexes, foreign keys, and other table constraints that will be executed against your database.

Basic Usage ​

Creating Tables ​

Use the Schema::create() method to create new tables:

php
<?php

use AvelPress\Database\Schema\Schema;
use AvelPress\Database\Schema\Blueprint;

Schema::create('users', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('email')->unique();
    $table->timestamp('email_verified_at')->nullable();
    $table->string('password');
    $table->timestamps();
});

Modifying Tables ​

Use Schema::table() to modify existing tables:

php
Schema::table('users', function (Blueprint $table) {
    $table->string('phone')->nullable();
    $table->string('address')->nullable();
});

Dropping Tables ​

Remove tables using Schema::drop():

php
// Drop table (will fail if table doesn't exist)
Schema::drop('posts');

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

Column Types ​

Numeric Columns ​

php
$table->id();                          // Auto-increment big integer primary key
$table->bigInteger('votes');           // BIGINT equivalent
$table->integer('votes');              // INTEGER equivalent
$table->mediumInteger('votes');        // MEDIUM INTEGER equivalent
$table->smallInteger('votes');         // SMALL INTEGER equivalent
$table->tinyInteger('votes');          // TINY INTEGER equivalent
$table->unsignedBigInteger('votes');   // Unsigned BIGINT equivalent
$table->unsignedInteger('votes');      // Unsigned INTEGER equivalent
$table->decimal('amount', 8, 2);       // DECIMAL equivalent with precision and scale
$table->float('amount', 8, 2);         // FLOAT equivalent with precision and scale
$table->double('amount', 8, 2);        // DOUBLE equivalent with precision and scale

String Columns ​

php
$table->string('name');                // VARCHAR equivalent with optional length
$table->string('name', 100);          // VARCHAR with specific length
$table->text('description');          // TEXT equivalent
$table->mediumText('description');    // MEDIUM TEXT equivalent
$table->longText('description');      // LONG TEXT equivalent
$table->char('code', 4);              // CHAR equivalent with length
$table->binary('data');               // BINARY equivalent

Date and Time Columns ​

php
$table->date('created_date');          // DATE equivalent
$table->dateTime('created_at');        // DATETIME equivalent
$table->dateTime('created_at', 3);     // DATETIME with precision
$table->time('sunrise');               // TIME equivalent
$table->timestamp('added_at');         // TIMESTAMP equivalent
$table->timestamps();                  // created_at and updated_at DATETIME columns
$table->year('birth_year');           // YEAR equivalent

Boolean and Other Types ​

php
$table->boolean('confirmed');          // BOOLEAN equivalent
$table->enum('level', ['easy', 'hard']); // ENUM equivalent
$table->json('options');               // JSON equivalent (MySQL 5.7+)
$table->uuid('identifier');           // UUID equivalent

Column Modifiers ​

Nullable and Default Values ​

php
$table->string('name')->nullable();              // Allow NULL values
$table->string('status')->default('active');     // Set default value
$table->integer('votes')->default(0);            // Numeric default
$table->boolean('confirmed')->default(true);     // Boolean default
$table->timestamp('created_at')->useCurrent();   // Use CURRENT_TIMESTAMP

Auto Increment ​

php
$table->id();                                    // Auto-increment primary key
$table->bigInteger('id')->autoIncrement();       // Custom auto-increment

Indexes ​

php
$table->string('email')->unique();               // Unique constraint
$table->string('name')->index();                 // Regular index
$table->index(['column1', 'column2']);           // Composite index
$table->unique(['first_name', 'last_name']);     // Composite unique
$table->index('email', 'idx_user_email');        // Named index

Comments ​

php
$table->string('name')->comment('User full name');
$table->integer('views')->comment('Page view count');

Column Positioning (MySQL) ​

php
$table->string('name')->after('id');             // Add column after another
$table->string('name')->first();                 // Add column as first

Primary Keys ​

Default Primary Key ​

php
$table->id(); // Creates auto-incrementing bigint primary key named 'id'

Custom Primary Keys ​

php
// Custom primary key name
$table->id('user_id');

// String primary key
$table->string('code', 10)->primary();

// Composite primary key
$table->primary(['user_id', 'post_id']);

// Non-incrementing primary key
$table->bigInteger('id')->primary();

Foreign Keys ​

Basic Foreign Keys ​

php
// Simple foreign key (references id on users table)
$table->foreignId('user_id')->constrained();

// Custom table reference
$table->foreignId('category_id')->constrained('categories');

// Custom column reference
$table->foreignId('author_id')->constrained('users', 'id');

// Manual foreign key definition
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('id')->on('users');

Foreign Key Actions ​

php
$table->foreignId('user_id')
    ->constrained()
    ->onUpdate('cascade')
    ->onDelete('cascade');

$table->foreignId('category_id')
    ->constrained()
    ->onDelete('set null');

$table->foreignId('parent_id')
    ->constrained('categories')
    ->onDelete('restrict');

Dropping Foreign Keys ​

php
Schema::table('posts', function (Blueprint $table) {
    $table->dropForeign(['user_id']);              // Drop by column
    $table->dropForeign('posts_user_id_foreign');  // Drop by constraint name
});

Indexes ​

Creating Indexes ​

php
Schema::table('users', function (Blueprint $table) {
    $table->index('email');                        // Single column index
    $table->index(['first_name', 'last_name']);    // Composite index
    $table->unique('username');                    // Unique index
    $table->index('created_at', 'idx_created');    // Named index
});

Index Types ​

php
$table->primary('id');                            // Primary key
$table->primary(['id', 'parent_id']);             // Composite primary
$table->unique('email');                          // Unique constraint
$table->index('name');                            // Regular index
$table->fullText('body');                         // Full-text index (MySQL)

Dropping Indexes ​

php
Schema::table('users', function (Blueprint $table) {
    $table->dropPrimary(['id']);                  // Drop primary key
    $table->dropUnique(['email']);                // Drop unique constraint
    $table->dropIndex(['email']);                 // Drop regular index
    $table->dropIndex('users_email_index');       // Drop by index name
});

Advanced Features ​

Conditional Schema Changes ​

php
// Check if table exists
if (Schema::hasTable('users')) {
    Schema::table('users', function (Blueprint $table) {
        // Modify existing table
    });
}

// Check if column exists
if (Schema::hasColumn('users', 'phone')) {
    Schema::table('users', function (Blueprint $table) {
        $table->dropColumn('phone');
    });
}

Renaming ​

php
// Rename table
Schema::rename('from', 'to');

// Rename column
Schema::table('users', function (Blueprint $table) {
    $table->renameColumn('from', 'to');
});

Raw SQL in Schema ​

php
Schema::table('users', function (Blueprint $table) {
    // Add custom SQL
    $table->addColumn('enum', 'status', [
        'values' => ['active', 'inactive', 'pending']
    ]);
});

// Execute raw SQL
DB::statement('ALTER TABLE users ADD FULLTEXT(first_name, last_name)');

WordPress Integration ​

Table Prefixes ​

AvelPress automatically handles WordPress table prefixes:

php
// This creates {wp_prefix}my_custom_table
Schema::create('my_custom_table', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->timestamps();
});

WordPress-Compatible Columns ​

php
Schema::create('posts', function (Blueprint $table) {
    $table->id('ID');                              // WordPress-style ID
    $table->string('post_title');
    $table->longText('post_content');
    $table->longText('post_excerpt');
    $table->string('post_status', 20)->default('publish');
    $table->string('post_type', 20)->default('post');
    $table->string('post_name');                   // Slug
    $table->unsignedBigInteger('post_author');
    $table->dateTime('post_date');
    $table->dateTime('post_date_gmt');
    $table->dateTime('post_modified');
    $table->dateTime('post_modified_gmt');
    $table->string('post_password')->default('');
    $table->integer('menu_order')->default(0);
    $table->longText('post_content_filtered');

    // WordPress indexes
    $table->index('post_name');
    $table->index('post_type');
    $table->index('post_status');
    $table->index('post_date');
    $table->index('post_author');
});

Foreign Keys to WordPress Tables ​

php
Schema::create('user_profiles', function (Blueprint $table) {
    $table->id();
    $table->unsignedBigInteger('user_id');
    $table->text('bio')->nullable();
    $table->string('website')->nullable();
    $table->timestamps();

    // Reference WordPress users table
    $table->foreign('user_id')->references('ID')->on('users')->onDelete('cascade');
    $table->unique('user_id');
});

Schema::create('post_meta_extended', function (Blueprint $table) {
    $table->id();
    $table->unsignedBigInteger('post_id');
    $table->string('meta_key');
    $table->longText('meta_value')->nullable();

    // Reference WordPress posts table
    $table->foreign('post_id')->references('ID')->on('posts')->onDelete('cascade');
    $table->index(['post_id', 'meta_key']);
});

Custom Post Type Tables ​

php
Schema::create('products', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->text('description');
    $table->decimal('price', 10, 2);
    $table->string('sku')->unique();
    $table->integer('stock_quantity')->default(0);
    $table->boolean('manage_stock')->default(false);
    $table->string('status')->default('publish');
    
    // Link to WordPress post
    $table->unsignedBigInteger('post_id')->nullable();
    $table->foreign('post_id')->references('ID')->on('posts')->onDelete('set null');
    
    $table->timestamps();
    
    // Indexes for performance
    $table->index('sku');
    $table->index('status');
    $table->index('price');
});

Migration Integration ​

In Migration Files ​

php
<?php

use AvelPress\Database\Migrations\Migration;
use AvelPress\Database\Schema\Blueprint;
use AvelPress\Database\Schema\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->decimal('price', 10, 2);
            $table->timestamps();
        });
    }

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

Complex Table Structures ​

php
public function up(): void
{
    Schema::create('orders', function (Blueprint $table) {
        $table->id();
        $table->string('order_number')->unique();
        $table->foreignId('customer_id')->constrained()->onDelete('cascade');
        $table->enum('status', ['pending', 'processing', 'shipped', 'delivered', 'cancelled'])
              ->default('pending');
        $table->decimal('subtotal', 10, 2);
        $table->decimal('tax_amount', 10, 2)->default(0);
        $table->decimal('shipping_cost', 10, 2)->default(0);
        $table->decimal('total_amount', 10, 2);
        $table->json('shipping_address');
        $table->json('billing_address');
        $table->timestamp('shipped_at')->nullable();
        $table->timestamp('delivered_at')->nullable();
        $table->timestamps();

        // Indexes for performance
        $table->index('order_number');
        $table->index('status');
        $table->index(['customer_id', 'status']);
        $table->index('created_at');
    });

    Schema::create('order_items', function (Blueprint $table) {
        $table->id();
        $table->foreignId('order_id')->constrained()->onDelete('cascade');
        $table->foreignId('product_id')->constrained()->onDelete('cascade');
        $table->string('product_name');  // Snapshot of product name
        $table->decimal('unit_price', 10, 2);
        $table->integer('quantity');
        $table->decimal('line_total', 10, 2);
        $table->timestamps();

        // Composite index for order items
        $table->index(['order_id', 'product_id']);
    });
}

Best Practices ​

1. Use Appropriate Data Types ​

php
// Good
$table->string('email', 100);          // Reasonable length for emails
$table->decimal('price', 10, 2);       // Precise decimal for money
$table->boolean('is_active');          // Clear boolean intent

// Avoid
$table->string('email');               // Unnecessarily long (255 chars)
$table->float('price');                // Imprecise for money
$table->tinyInteger('is_active');      // Unclear boolean intent

2. Add Appropriate Indexes ​

php
Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->string('title');
    $table->text('content');
    $table->string('slug')->unique();   // Unique index for slugs
    $table->string('status')->index();  // Index for frequent queries
    $table->foreignId('author_id')->constrained('users');
    $table->timestamps();

    // Composite indexes for common query patterns
    $table->index(['status', 'created_at']);
    $table->index(['author_id', 'status']);
});

3. Design for WordPress Compatibility ​

php
Schema::create('plugin_settings', function (Blueprint $table) {
    $table->id();
    $table->string('option_name')->unique();
    $table->longText('option_value');
    $table->string('autoload', 20)->default('yes');
    $table->timestamps();

    // Similar structure to wp_options
    $table->index('option_name');
    $table->index('autoload');
});

4. Handle Character Sets ​

php
// WordPress uses utf8mb4 by default
Schema::create('comments', function (Blueprint $table) {
    $table->id();
    $table->longText('content');  // Supports emojis and special characters
    $table->string('author_name');
    $table->string('author_email');
    $table->timestamps();
});

5. Foreign Key Considerations ​

php
// Good: Handles cascading deletes properly
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->foreignId('category_id')->constrained()->onDelete('set null');

// Be careful with WordPress core tables
$table->unsignedBigInteger('wp_user_id');
$table->foreign('wp_user_id')->references('ID')->on('users')->onDelete('cascade');

The Schema Builder provides a powerful and WordPress-compatible way to manage your database structure, ensuring your tables work seamlessly with both AvelPress and WordPress core functionality.

Released under the MIT License.