Laravel seed data

Laravel + PostgreSQL test data beyond DatabaseSeeder and factories — distributions and FKs from the schema itself.

Laravel projects seed through database/seeders/DatabaseSeeder.php, run by php artisan db:seed — usually model factories plus a loop of Model::create() calls. It works until relations and constraints outgrow the seeder. This guide shows the Laravel-native seeding workflow, where it breaks, and the schema-driven alternative.

How Laravel seeding normally works

Laravel owns the schema through migrations (php artisan migrate). Seeding is a seeder class, run with php artisan db:seed (or migrate:fresh --seed):

// database/seeders/DatabaseSeeder.php
namespace Database\Seeders;
 
use App\Models\User;
use Illuminate\Database\Seeder;
 
class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        for ($i = 0; $i < 50; $i++) {
            User::create([
                'email' => fake()->unique()->safeEmail(),
                'name' => fake()->name(),
            ]);
        }
    }
}

Works for a standalone model. The moment Order has a belongsTo relation, the seeder has to create the user first and attach it — dependency order becomes something the seeder encodes by hand.

Where Laravel seed scripts break

  1. Foreign keys. Order::create(['user_id' => ...]) needs a persisted parent id. The seeder grows ordering logic and lookups, and every added relation is another line of bookkeeping. Miss one and you get a foreign-key violation or an orphaned row.
  2. Schema drift. A new non-null column, a cast, a validation rule — each one is a seeder change. The migration and model move; DatabaseSeeder does not, and tests start failing for reasons nobody can explain.
  3. Uniform randomness. fake() produces plausible values, not production distributions. If your real status column is 70% active, a flat loop gives you roughly a third of each — and query plans in tests stop matching production.

Schema-driven seeding with Weavori

Weavori reads the database schema — the one Laravel migrated — and generates rows from it. The Laravel-specific workflow:

1. Migrate as usual
$ php artisan migrate
2. Seed from the schema
$ npx --yes @weavori/cli generate postgres://localhost:5432/mydb --rows 1000

Weavori introspects tables, foreign keys, types, and constraints, resolves dependency order (parents before children), and writes via COPY. Every generated order.user_id references a real generated user — by construction, not by seeder ordering.

The schema is the seed

When a Laravel migration changes the schema, the generated data changes with it. There is no seeder to update because nothing in it duplicated the schema.

Laravel + Weavori in CI

The loop that keeps test databases honest:

$ php artisan migrate --force
$ npx --yes @weavori/cli generate $TEST_DATABASE_URL --rows 1000 --output plain

Weavori supports API keys for headless auth, standardized exit codes, and offline license validation — CI runners on restricted networks don't phone home. Full GitHub Actions / GitLab / CircleCI / Jenkins examples in the CI/CD guide.

Which Laravel seed command do you need?

GoalCommand
Run DatabaseSeeder against an existing databasephp artisan db:seed
Migrate, then seedphp artisan migrate --seed
Drop every table, re-migrate, then seedphp artisan migrate:fresh --seed
Run a single seeder classphp artisan db:seed --class=UserSeeder
Generate a production-shaped dataset with no seedernpx --yes @weavori/cli generate postgres://localhost:5432/mydb

php artisan db:seed

php artisan db:seed runs Database\Seeders\DatabaseSeeder, which calls the seeder classes it lists. Artisan supplies the runner and Eloquent supplies the models; the rows come from the seeder, so the seeder is where relation bookkeeping accumulates as belongsTo and hasMany relationships appear.

php artisan migrate --seed

migrate --seed applies pending migrations and then runs the seeder in one command — the standard deploy-time pair. Seeding after migration is not optional: the seeder writes into the columns the migration just created.

php artisan migrate:fresh --seed

migrate:fresh drops every table, re-runs all migrations from scratch, and with --seed repopulates in the same pass. It is the clean-slate command — and the one that exposes a seeder which only works against a database somebody has already filled by hand. migrate:refresh --seed is the gentler variant: it rolls migrations back instead of dropping the tables.

php artisan db:seed --class=UserSeeder

--class runs one seeder instead of the default. It is useful while iterating on a single table, and a reminder that Laravel expects seeding to be split into classes per concern — more files, each holding its own copy of what the schema already declares.

When to keep your Laravel seeder

Honesty about boundaries: keep DatabaseSeeder for deterministic unit-test fixtures — a known user the test asserts against. Keep model factories for single values inside tests. Weavori replaces the dataset problem: filling dev, staging, and CI databases with production-shaped, FK-intact rows.

Ready to generate your first dataset?

Install Weavori in one command and connect to any PostgreSQL database.

$npm install -g @weavori/cli
macOS · Linux · Windows/No dependencies required

No credit card required. Start with a 14-day free trial of Pro, then free tier or subscribe.