Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

greenie2600's avatar

Best way to seed core, non-test data?

I'm familiar with using migrations to modify the database schema, and with using seeders to populate test data.

However, I'm trying to do something slightly different: I want to populate real data into the app at install time (and as I roll out new features).

For example, let's say my app has a State model. States are not editable within the app; they're effectively constant data. So they need to be populated as part of the install process.

As another example, I'd like to create a default admin user at install time. And maybe later I'll add a Country model, which means that I'll need to populate that table as part of the upgrade process.

What's the best approach to this? Seeders don't seem right, for a few different reasons.

Migrations seem like a better choice: when I get around to implementing the Country feature, I can just run the migration on each instance of the app, and (in theory) the migration will create the new table and populate it.

However, I haven't seen any examples of this – so I wonder whether it's a bad idea (or perhaps not even possible).

Any thoughts?

0 likes
5 replies
willvincent's avatar

I've got some seeders for precisely this purpose.

This is a pretty basic example, but it ensures these small handful of countries get populated into the DB on initial db:seed, and since it uses firstOrCreate they're only created once.. this way I can run db:seed as part of my deploy and not worry about issues despite the fact that I've got a few different seeders. firstOrCreate ensures the seed only does anything if the entry doesn't already exist.

<?php

use App\Models\Country;
use Illuminate\Database\Seeder;

class CountriesTableSeeder extends Seeder
{

    private $countries = ['USA', 'Bulgaria', 'Serbia', 'Ukraine', 'UK', 'Turkey'];

    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        foreach ($this->countries as $country) {
            Country::firstOrCreate(['name' => $country]);
        }
    }
}

I have a couple others that are a bit more complex, but the general approach is the same. I think it works exactly as I'd like it to.. migrations -- to my mind -- are for table structure, not table content.

3 likes
greenie2600's avatar

Thanks, willvincent! That's perfect. I didn't know about the firstOrCreate() method. It's documented on the Eloquent page in the docs, but isn't mentioned in the section about seeding.

Building on this, I think I'll tweak the default seeder setup thusly:

  1. Modify the default DatabaseSeeder class to have an empty run() method (so that running php artisan db:seed does nothing).
  2. Create a new Seeder, named CoreDatabaseSeeder, which populates the States table, etc., using firstOrCreate() (which I can run with php artisan db:seed --class=CoreDatabaseSeeder).
  3. Create another new Seeder, named TestDatabaseSeeder, which populates test data (e.g., users, comments, etc.). This one would first check to see whether env('APP_ENV') === 'production', to prevent me from accidentally wiping my production database. I would run this with php artisan db:seed --class=TestDatabaseSeeder.

Does this seem like a reasonable approach?

(xdimension, I'll take a closer look at SmartSeeder – seems like it could be useful if my needs become more complex. Right now I'm trying to keep things simple.)

1 like
martinbean's avatar

@greenie2600 The db:seed command takes an optional --class option. So you could define a seeder classed called ProductionTableSeeder that in turn calls other seeder classes.

greenie2600's avatar

Yep, that was the gist of it. I believe this solves my problem. Thanks to everyone for your help! This seems like a good community.

Please or to participate in this conversation.