LARAVEL

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.