Laravel Database Migrations: Complete Guide
February 20, 2024
•
18 min read
Introduction
Laravel migrations allow you to manage your database schema programmatically. They provide version control for your database structure, making it easy to modify and share database designs across your team.
Creating Migrations
Generate Migration
// Create new migration
php artisan make:migration create_users_table
// Create with table name
php artisan make:migration create_users_table --table=users
// Create for existing table
php alter_users_table
Migration Structure
// database/migrations/2024_01_01_000000_create_users_table.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('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('users');
}
};
Schema Builder
Column Types
// Common column types
$table->id();
$table->bigInteger('user_id');
$table->integer('quantity');
$table->string('name', 100);
$table->text('description');
$table->boolean('active');
$table->date('birth_date');
$table->datetime('created_at');
$table->timestamp('updated_at');
$table->decimal('price', 8, 2);
$table->float('latitude', 10, 8);
$table->enum('status', ['pending', 'active', 'completed']);
$table->json('metadata');
$table->uuid('uuid');
$table->morphs('taggable');
Column Modifiers
// Column modifiers
$table->string('email')->unique();
$table->integer('votes')->default(0)->nullable();
$table->string('name')->nullable(false)->change();
$table->string('nickname')->nullable();
$table->text('bio')->after('name');
$table->integer('order')->first();
Indexes
// Create indexes
$table->primary('id');
$table->unique('email');
$table->index('name');
$table->index(['first_name', 'last_name']);
$table->fullText('description'); // MySQL
$table->spatialIndex('location'); // PostgreSQL
Modifying Tables
Rename & Drop
// Rename table
Schema::rename('old_users', 'users');
// Drop table
Schema::drop('users');
Schema::dropIfExists('users');
Add Columns
// Add new column
Schema::table('users', function (Blueprint $table) {
$table->string('phone')->after('email');
$table->boolean('is_admin')->default(false);
});
Modify Columns
// Enable modification
Schema::enableForeignKeyConstraints();
// Modify column (requires doctrine/dbal)
Schema::table('users', function (Blueprint $table) {
$table->string('name', 100)->change();
$table->renameColumn('name', 'full_name');
});
// Drop column
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('phone');
$table->dropColumns(['phone', 'fax']);
});
Database Seeders
Create Seeder
php artisan make:seeder UserSeeder
// database/seeders/UserSeeder.php
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class UserSeeder extends Seeder
{
public function run(): void
{
DB::table('users')->insert([
'name' => 'John Doe',
'email' => 'john@example.com',
'password' => bcrypt('password'),
]);
}
}
Model Factories
php artisan make:factory UserFactory
// database/factories/UserFactory.php
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
class UserFactory extends Factory
{
protected $model = User::class;
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'password' => bcrypt('password'),
'email_verified_at' => now(),
];
}
}
// Usage
User::factory()->count(10)->create();
Run Seeders
// Run specific seeder
php artisan db:seed --class=UserSeeder
// Migrate and seed
php artisan migrate:fresh --seed
// Seed without migration
php artisan db:seed
Foreign Keys
Creating Foreign Keys
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')
->constrained()
->onDelete('cascade');
$table->string('title');
$table->timestamps();
});
// Or using references
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')
->references('id')
->on('users')
->onDelete('cascade');
Foreign Key Constraints
// Restrict delete
$table->foreignId('user_id')
->constrained()
->onDelete('restrict');
// Set null on delete
$table->foreignId('user_id')
->nullable()
->constrained()
->onDelete('set null');
Best Practices
- One change per migration - Keep migrations focused and atomic
- Always include rollback - The down() method must work
- Use meaningful names - Name migrations descriptively
- Test in development first - Verify migrations before production
- Never modify existing migrations - Create new ones instead
- Use transactions - Wrap related operations
Summary
Laravel migrations provide a robust way to manage your database schema. Combined with seeders and factories, you can easily set up test data and maintain consistent database structures across environments.
For more Laravel tutorials, check out Advanced Laravel Eloquent and Laravel Performance Optimization.