skip to content

In Laravel, how do you organise and run database seeders, and what do db:seed --class and migrate --seed do?

level: juniorimportance: must knowfreq 58%

answer

  1. a class with a run() method
  2. DatabaseSeeder is the root
  3. $this->call([...]) runs others in order
  4. db:seed --class=DoctorSeeder
  5. migrate --seed, --seeder=Class

basics

~20 s

A seeder is a class in database/seeders with a run() method; DatabaseSeeder is the root and runs others through $this->call(). php artisan db:seed runs DatabaseSeeder, --class runs one seeder, and migrate --seed seeds after migrating.

solid answer

~40 s

`php artisan make:seeder DoctorSeeder` creates a class extending `Illuminate\Database\Seeder` whose `run()` method inserts data, usually with model factories. `Database\Seeders\DatabaseSeeder` is the root: its `run()` calls `$this->call([DepartmentSeeder::class, DoctorSeeder::class])`, which runs them in that order, so parents exist before children. `php artisan db:seed` runs the root; `--class=DoctorSeeder` runs one class (a bare name is prefixed with `Database\Seeders\`), and `--database=` picks a connection. `php artisan migrate --seed` runs pending migrations and then `db:seed`, with `--seeder=` choosing another root; `migrate:fresh --seed` rebuilds the schema and seeds it. `db:seed` wraps the run in `Model::unguarded()`, and in the production environment it asks for confirmation unless you pass `--force`.

code

php · 19 lines
php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        // Parents first: appointments need doctors and patients
        $this->call([
            DepartmentSeeder::class,
            DoctorSeeder::class,
            PatientSeeder::class,
            AppointmentSeeder::class,
        ]);
    }
}

go deeper

for a junior

Recall make:seeder, the run() method, DatabaseSeeder as the root, $this->call() for ordering, and db:seed with --class.

for a middle

Explain how migrate --seed, --seeder and migrate:fresh --seed combine, and why call() order must follow foreign keys.

for a senior

Show deploy awareness: the production confirmation, --force in non-interactive runs, and why re-running random demo seeders doubles data.

for a principal

Decide what seeding means per environment, keeping demo data, local fixtures and required production rows as separate seeders with separate entry points.

## What a seeder is A **seeder** is a PHP class in `database/seeders` that extends `Illuminate\Database\Seeder` and defines a `run()` method. Whatever `run()` does is the seeding: it usually calls **model factories** to build realistic fake records, and it can also use the query builder for fixed rows. `php artisan make:seeder DoctorSeeder` generates an empty class. `run()` is invoked through the service container, so you may type-hint dependencies in its signature and they are injected. ## The root seeder and call() Every application has one **root seeder**, `Database\Seeders\DatabaseSeeder`. The skeleton's version creates a single test user with `User::factory()->create([...])`. As the data grows, the root becomes a table of contents: - `$this->call([DepartmentSeeder::class, DoctorSeeder::class, AppointmentSeeder::class])` runs each class in the listed order, which is how you guarantee parents exist before the children that reference them; - `$this->callOnce(...)` skips a seeder that has already run in the same process; - `$this->callSilent(...)` runs without printing progress lines; - `$this->callWith(DoctorSeeder::class, ['count' => 20])` passes parameters to the called seeder's `run()`. Splitting seeders by aggregate keeps each one small and lets you re-run just one. ## Running seeders from Artisan | Command | What it runs | |---|---| | `php artisan db:seed` | `Database\Seeders\DatabaseSeeder` | | `php artisan db:seed --class=DoctorSeeder` | only `Database\Seeders\DoctorSeeder` (namespace added for a bare name) | | `php artisan db:seed --database=reporting` | the root seeder against another connection | | `php artisan migrate --seed` | pending migrations, then `db:seed` with the root | | `php artisan migrate --seed --seeder=DemoSeeder` | pending migrations, then `DemoSeeder` | | `php artisan migrate:fresh --seed` | drop all tables, migrate from scratch, then seed | Two details trip people up: 1. On plain `migrate`, `--seeder=` only picks the class; seeding happens only when `--seed` is also given. `migrate:fresh` treats either flag as a request to seed. 2. `migrate --pretend --seed` does not seed, because a pretend run never executes the migrations. ## What db:seed does around your code 1. **Confirmation in production.** When the application environment is `production`, `db:seed` prints a warning and asks for confirmation; `--force` skips the prompt. In a non-interactive run the prompt's default answer is no, so the command cancels and exits with a failure code. `migrate --seed` passes `--force` to the `db:seed` it calls, relying on `migrate`'s own production confirmation. 2. **Unguarded models.** The whole run is wrapped in `Model::unguarded()`, so `Model::create([...])` inside a seeder is not filtered by `$fillable`. 3. **Connection switch.** With `--database`, the default connection is swapped for the run and restored afterwards, even if the seeder throws. ## A hospital demo layout For a hospital-appointments app a typical layout is: `DepartmentSeeder` (a handful of departments), `DoctorSeeder` (doctors per department), `PatientSeeder`, and `AppointmentSeeder` (appointments that reference existing doctors and patients). `DatabaseSeeder::run()` calls them in that order, so `php artisan migrate:fresh --seed` rebuilds a complete, browsable demo in one command, and `db:seed --class=AppointmentSeeder` tops up appointments without touching the rest. ## Seeders and factories together A seeder does not have to use factories, but most demo seeders do, because a factory already knows how to produce a valid record. Typical patterns inside `run()`: - `Department::factory()->count(6)->create()` for bulk records with random attributes; - `Doctor::factory()->create(['email' => '[email protected]'])` for one known account a developer can log in as; - `Appointment::factory()->count(300)->recycle($doctors)->create()` for children that reuse parents created earlier in the same run. Seeders are also where you decide volume. A demo that loads in seconds keeps `migrate:fresh --seed` part of everyday work; one that takes minutes gets skipped, and the team drifts onto stale local databases. Because each seeder is a class, a team can keep a small `DatabaseSeeder` for daily work and a separate, heavier root such as `LoadTestSeeder` that is run explicitly with `--class` or `--seeder` when someone needs volume. ## Common mistakes - Relying on the order of files on disk: only `call()` order matters, and `db:seed` never discovers seeders on its own. - Expecting `db:seed` to clear tables first: it only runs your code, so running it twice doubles random demo data. - Scheduling a production deploy step with `db:seed` and no `--force`: the non-interactive run cancels and fails the step.

  • Why might `php artisan migrate --seeder=DemoSeeder` migrate but not seed?
    On `migrate`, `--seeder` only names the root class; the command seeds only when `--seed` is also present. `migrate:fresh` is different and seeds when either flag is given. Add `--seed` to the `migrate` call.
  • What happens when a CI step runs `php artisan db:seed` with APP_ENV=production and no --force?
    `db:seed` asks for confirmation in production. In a non-interactive run the default answer is no, so it prints that the command was cancelled and exits with a failure code, which fails the step. Pass `--force` when seeding there is intended.
  • How do you run a seeder only once when several parent seeders call it?
    Call it with `$this->callOnce(PatientSeeder::class)`. The base `Seeder` records every class it has run in the current process and `callOnce()` skips any that already ran, so shared prerequisites are not seeded twice.

saying these in an interview costs you the question

  • db:seed runs every class in database/seeders automatically
  • Seeders run in alphabetical order of their file names
  • db:seed truncates the tables before inserting
  • migrate --seed drops every table before migrating
  • --force is needed to seed in every environment, not just production