Laravel Migrations
Laravel migrations are version control for your database. Each migration is a PHP class describing a schema change, so your whole team and every server can build the same tables with one command instead of running SQL by hand.
Creating a Migration
php artisan make:migration create_posts_tableINFO Migration [database/migrations/2026_10_31_120000_create_posts_table.php] created successfully.
Defining Columns
The up method applies the change; down reverses it. The Schema builder has a method for every column type.
<?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->enum('status', ['draft', 'published'])->default('draft');
$table->timestamp('published_at')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('posts');
}
};posts: id, user_id (FK users.id), title, slug (unique), body, status, published_at, created_at, updated_at
Running and Rolling Back Laravel Migrations
php artisan migrate # run new migrations
php artisan migrate:status # see what ran
php artisan migrate:rollback # undo the last batch
php artisan migrate:fresh --seed # drop all tables, re-run, seed (dev only!)INFO Running migrations. 2026_10_31_120000_create_posts_table ........ 18ms DONE
Changing an Existing Table
Never edit a migration that already ran in production. Create a new one that alters the table.
php artisan make:migration add_views_to_posts_table
<?php
public function up(): void
{
Schema::table('posts', function (Blueprint $table) {
$table->unsignedInteger('views')->default(0)->after('body');
$table->index('published_at');
});
}
public function down(): void
{
Schema::table('posts', function (Blueprint $table) {
$table->dropIndex(['published_at']);
$table->dropColumn('views');
});
}INFO Running migrations. 2026_10_31_130000_add_views_to_posts_table .... 9ms DONE
Seeders and Factories
Factories generate fake model data and seeders insert it. They are perfect for local development and tests.
<?php
// database/seeders/DatabaseSeeder.php
public function run(): void
{
User::factory()
->count(5)
->has(Post::factory()->count(3))
->create();
}
// php artisan db:seedINFO Seeding database. (5 users and 15 posts created)
Frequently asked questions
How do I add a column to an existing table in Laravel?
Create a new migration with make:migration add_column_to_table_table, use Schema::table() to add the column, then run php artisan migrate.
What is the difference between migrate:fresh and migrate:refresh?
migrate:fresh drops all tables and re-runs every migration. migrate:refresh rolls back each migration using down() then runs them again. Both destroy data.
What does constrained() do?
On a foreignId column it adds a foreign key to the conventional table, so user_id references id on users.