<?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('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('title');
$table->string('slug')->unique();
$table->text('body');
$table->string('excerpt')->nullable();
$table->string('status')->default('draft');
$table->boolean('is_featured')->default(false);
$table->integer('views')->default(0);
$table->timestamp('published_at')->nullable();
$table->timestamps();
$table->softDeletes();
// Indexes
$table->index(['status', 'published_at']);
$table->index('user_id');
$table->fullText(['title', 'body']); // MySQL only
});
}
public function down(): void
{
Schema::dropIfExists('posts');
}
};
<?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::table('posts', function (Blueprint $table) {
$table->foreignId('category_id')
->nullable()
->after('user_id')
->constrained()
->nullOnDelete();
});
}
public function down(): void
{
Schema::table('posts', function (Blueprint $table) {
$table->dropForeign(['category_id']);
$table->dropColumn('category_id');
});
}
};
<?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('post_tag', function (Blueprint $table) {
$table->foreignId('post_id')->constrained()->cascadeOnDelete();
$table->foreignId('tag_id')->constrained()->cascadeOnDelete();
$table->integer('order')->default(0);
$table->timestamps();
$table->primary(['post_id', 'tag_id']);
});
}
public function down(): void
{
Schema::dropIfExists('post_tag');
}
};
Migrations version control database schema changes, making them trackable and reversible. Each migration file contains up() and down() methods defining changes and rollbacks. I use the Schema builder's fluent API to create tables, add columns, define indexes, and set foreign keys. Running php artisan migrate executes pending migrations in order. The --step flag enables selective rollbacks. Migration files never edit after committing—create new ones to modify schema. For data migrations, I use seeders or separate migration files. Anonymous migrations reduce file clutter. Database-specific features use DB::statement() for raw SQL. Schema dumps capture current state for faster fresh installs. Migrations enable team collaboration without manual schema coordination.