Open-Source Wikis

/

Laravel

/

Systems

/

Database

laravel/laravel

Database

Active contributors: Taylor Otwell, Jack Bayliss, Nuno Maduro

Purpose

database/ ships the schema, fixtures, and seeders. Three migrations define the ten baseline tables that the framework's defaults depend on; one factory produces fake users; one seeder seeds a single test user. SQLite is the default connection — the .env.example points there and the composer.json post-create-project-cmd touches database/database.sqlite so it exists out of the box.

Directory layout

database/
├── .gitignore                              # Excludes database.sqlite
├── factories/
│   └── UserFactory.php
├── migrations/
│   ├── 0001_01_01_000000_create_users_table.php
│   ├── 0001_01_01_000001_create_cache_table.php
│   └── 0001_01_01_000002_create_jobs_table.php
└── seeders/
    └── DatabaseSeeder.php

The three baseline migrations

The skeleton uses anonymous-class migrations (return new class extends Migration) — no class names to manage. Each file groups related tables.

0001_01_01_000000_create_users_table.php

Creates three tables in one migration:

Table Columns Used by
users id, name, email (unique), email_verified_at (nullable), password, remember_token, timestamps App\Models\User, web auth guard
password_reset_tokens email (PK), token, created_at (nullable) Auth::sendPasswordResetLink()
sessions id (PK), user_id (nullable, indexed), ip_address, user_agent, payload (longText), last_activity (indexed) database session driver

The down() method drops all three in reverse order.

0001_01_01_000001_create_cache_table.php

Table Columns Used by
cache key (PK), value (mediumText), expiration (indexed) database cache store
cache_locks key (PK), owner, expiration (indexed) Cache::lock() atomic locks

0001_01_01_000002_create_jobs_table.php

Table Columns Used by
jobs id, queue (indexed), payload, attempts, reserved_at (nullable), available_at, created_at database queue connection
job_batches id (PK), name, total_jobs, pending_jobs, failed_jobs, failed_job_ids, options, cancelled_at, created_at, finished_at Bus::batch(...) job batches
failed_jobs id, uuid (unique), connection, queue, payload, exception, failed_at (defaults to current ts) database-uuids failed-job driver

Note that v12.11.2 (CHANGELOG.md) merged "Update jobs/cache migrations" and "Remove failed jobs indexes" PRs (#6736, #6739). The current files are the result of that consolidation.

Schema diagram

erDiagram
    USERS ||--o{ SESSIONS : has
    USERS {
        bigint id PK
        string name
        string email UK
        timestamp email_verified_at
        string password
        string remember_token
        timestamps timestamps
    }
    PASSWORD_RESET_TOKENS {
        string email PK
        string token
        timestamp created_at
    }
    SESSIONS {
        string id PK
        bigint user_id FK
        string ip_address
        text user_agent
        longtext payload
        int last_activity
    }
    CACHE {
        string key PK
        mediumtext value
        bigint expiration
    }
    CACHE_LOCKS {
        string key PK
        string owner
        bigint expiration
    }
    JOBS {
        bigint id PK
        string queue
        longtext payload
        smallint attempts
        int reserved_at
        int available_at
        int created_at
    }
    JOB_BATCHES {
        string id PK
        string name
        int total_jobs
        int pending_jobs
        int failed_jobs
        longtext failed_job_ids
        mediumtext options
        int cancelled_at
        int created_at
        int finished_at
    }
    FAILED_JOBS {
        bigint id PK
        string uuid UK
        text connection
        text queue
        longtext payload
        longtext exception
        timestamp failed_at
    }

database/factories/UserFactory.php

Defines a default state for App\Models\User:

public function definition(): array
{
    return [
        'name' => fake()->name(),
        'email' => fake()->unique()->safeEmail(),
        'email_verified_at' => now(),
        'password' => static::$password ??= Hash::make('password'),
        'remember_token' => Str::random(10),
    ];
}

The cached static $password ensures every fake user shares the same bcrypt hash, which is the slow operation. Subsequent factory calls reuse it instead of re-hashing.

A second helper:

public function unverified(): static
{
    return $this->state(fn (array $attributes) => [
        'email_verified_at' => null,
    ]);
}

Used in tests with User::factory()->unverified()->create().

database/seeders/DatabaseSeeder.php

public function run(): void
{
    // User::factory(10)->create();

    User::factory()->create([
        'name' => 'Test User',
        'email' => 'test@example.com',
    ]);
}

The seeder uses WithoutModelEvents (so creating/created events don't fire during seeding), and creates a single deterministic test user. The commented-out User::factory(10)->create() line is a hint for adding bulk fixtures.

Run with:

php artisan db:seed
# or, fresh DB + seed:
php artisan migrate:fresh --seed

Default connection

config/database.php defaults default to env('DB_CONNECTION', 'sqlite'). The SQLite block:

'sqlite' => [
    'driver' => 'sqlite',
    'url' => env('DB_URL'),
    'database' => env('DB_DATABASE', database_path('database.sqlite')),
    'prefix' => '',
    'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
    'busy_timeout' => null,
    'journal_mode' => null,
    'synchronous' => null,
    'transaction_mode' => 'DEFERRED',
],

The database/database.sqlite file is gitignored. The composer.json post-create-project-cmd block creates it via touch on a fresh install.

For tests, phpunit.xml overrides DB_CONNECTION=sqlite and DB_DATABASE=:memory: so each test run starts with an empty in-memory database. Tests that need persistence between assertions use Laravel's RefreshDatabase trait.

Adding a migration

php artisan make:migration create_posts_table

That creates a timestamped file in database/migrations/ extending Migration as an anonymous class. Define up() and down() symmetrically; rerun with:

php artisan migrate          # forward
php artisan migrate:rollback # back one batch
php artisan migrate:fresh    # drop everything, re-run

Migrations are tracked in a migrations table (config/database.php'migrations' => ['table' => 'migrations']).

Integration points

  • config/database.php picks the connection. Switching from SQLite to MySQL is purely an env-var change.
  • config/cache.php's database store, config/session.php's database driver, and config/queue.php's database connection all point at this same default DB.
  • App\Models\User maps to the users table.

Entry points for modification

To add a feature that needs storage: write a migration in database/migrations/, add the model to app/Models/, add the factory to database/factories/, and (optionally) seed fixtures from database/seeders/DatabaseSeeder.php. The seeder is the integration point — it calls $this->call(OtherSeeder::class) to chain in dedicated seeders for each feature.

Key source files

File Purpose
database/migrations/0001_01_01_000000_create_users_table.php users, password_reset_tokens, sessions
database/migrations/0001_01_01_000001_create_cache_table.php cache, cache_locks
database/migrations/0001_01_01_000002_create_jobs_table.php jobs, job_batches, failed_jobs
database/factories/UserFactory.php Faker-driven User state
database/seeders/DatabaseSeeder.php Creates test@example.com

See App for the User model and Reference → Data models for a full list of fields and casts.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Database – Laravel wiki | Factory