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.