ChatGPT解决这个技术问题 Extra ChatGPT

Populating a database in a Laravel migration file

I'm just learning Laravel, and have a working migration file creating a users table. I am trying to populate a user record as part of the migration:

public function up()
{
    Schema::create('users', function($table){

        $table->increments('id');
        $table->string('email', 255);
        $table->string('password', 64);
        $table->boolean('verified');
        $table->string('token', 255);
        $table->timestamps();

        DB::table('users')->insert(
            array(
                'email' => `name@domain.example`,
                'verified' => true
            )
        );

    });
}

But I'm getting the following error when running php artisan migrate:

SQLSTATE[42S02]: Base table or view not found: 1146 Table 'vantage.users' doesn't exist

This is obviously because Artisan hasn't yet created the table, but all the documentation seems to say that there is a way of using Fluent Query to populate data as part of a migration.

Anyone know how?


S
Stephen Ostermiller

Don't put the DB::insert() inside of the Schema::create(), because the create method has to finish making the table before you can insert stuff. Try this instead:

public function up()
{
    // Create the table
    Schema::create('users', function($table){
        $table->increments('id');
        $table->string('email', 255);
        $table->string('password', 64);
        $table->boolean('verified');
        $table->string('token', 255);
        $table->timestamps();
    });

    // Insert some stuff
    DB::table('users')->insert(
        array(
            'email' => 'name@domain.example',
            'verified' => true
        )
    );
}

and how to insert multiple data ?
@SuperMario'sYoshi i think something like this DB::table('users')->insert([ ['email' => 'taylor@example.com', 'votes' => 0], ['email' => 'dayle@example.com', 'votes' => 0] ]);
v
vtamm

I know this is an old post but since it comes up in a google search I thought I'd share some knowledge here. @erin-geyer pointed out that mixing migrations and seeders can create headaches and @justamartin countered that sometimes you want/need data to be populated as part of your deployment.

I'd go one step further and say that sometimes it is desirable to be able to roll out data changes consistently so that you can for example deploy to staging, see that all is well, and then deploy to production with confidence of the same results (and not have to remember to run some manual step).

However, there is still value in separating out the seed and the migration as those are two related but distinct concerns. Our team has compromised by creating migrations which call seeders. This looks like:

public function up()
{
    Artisan::call( 'db:seed', [
        '--class' => 'SomeSeeder',
        '--force' => true ]
    );
}

This allows you to execute a seed one time just like a migration. You can also implement logic that prevents or augments behavior. For example:

public function up()
{
    if ( SomeModel::count() < 10 )
    {
        Artisan::call( 'db:seed', [
            '--class' => 'SomeSeeder',
            '--force' => true ]
        );
    }
}

This would obviously conditionally execute your seeder if there are less than 10 SomeModels. This is useful if you want to include the seeder as a standard seeder that executed when you call artisan db:seed as well as when you migrate so that you don't "double up". You may also create a reverse seeder so that rollbacks works as expected, e.g.

public function down()
{
    Artisan::call( 'db:seed', [
        '--class' => 'ReverseSomeSeeder',
        '--force' => true ]
    );
}

The second parameter --force is required to enable to seeder to run in a production environment.


This is by far the best answer. Maintainable code that separates concerns!
I'd be careful to consider the long term implications of calling seeders from migration scripts. The migration scripts are date/time versioned, while seeders are typically not. During development, seeder needs often change, resulting in the possibility of versioned migration scripts running non-versioned seeders -- breaking idempotency. In other words, running the same set of migration scripts from day to day could yield different results.
It's been a while since I posted this and I wanted to provide our experience using this technique. Overall it has worked well for us and if I had to do it over again I would. That said there is one gotcha to be aware of. @originalbryan is exactly right and the consequence is that we occasionally run into situations where migrations break when spinning up a fresh DB because as the migrations run the seeder (and model) are more up-to-date than the database (since we may seed before the schema is fully updated). When that happens we update the old migration to address the issue.
@darrylkuhn yes, your proposition break that rule. If we not call seeder inside migration file but put 'INSERT-s' directly in migration file then we have saved data consistency without changing old migration files. In fact i clearly not see problem with putting INSERT in migration files - e.g. i have system with 30 tables, and want add new feature which need new table with some initial data - so I put that initial data in migration file (in separate method) - no problem :P
All of Laravel's language implies a seeder is for test data, so I think that should be kept in mind with design. It's important to distinguish between data that is part of the app vs test data, and including required data directly in a migration makes that distinction very clearly.
t
totymedli

Here is a very good explanation of why using Laravel's Database Seeder is preferable to using Migrations: https://web.archive.org/web/20171018135835/http://laravelbook.com/laravel-database-seeding/

Although, following the instructions on the official documentation is a much better idea because the implementation described at the above link doesn't seem to work and is incomplete. http://laravel.com/docs/migrations#database-seeding


I agree with you Erin. Don't mix migrations with seed data because it's highly likely that you would like to seed some data in your development environment but not in your production environment.
Good point, but there are some situations where some data must exist in production environment. For example, the very first default admin user must exist so the customer can log-in for the first time, some preset authorization roles must exist, some business-logic data also might be required immediately. Thus, I think mandatory data should be added to migrations (so that you can up/down also data records through separate migrations), but seeds can be left for development.
A small note; the link to database seeding is now: laravel.com/docs/5.3/seeding
Please include the relevant part of the linked articles. Link only answers are discouraged. Your first link is already dead and I had to bring it back from archive.org!
M
Martin Mbae

If you are using Laravel 8 and would would like to initialize with multiple records you can do it in any of these two ways.

1. The Not Recommended Way

public function up()
    {
        Schema::create('categories', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->timestamps();
        });

        DB::table('categories')->insert(
            array(
                [
                    'name' => 'Category1',
                ],
                [
                    'name' => 'Category2',
                ],
                [
                    'name' => 'Category3',
                ],
            )
        );
    }

The above method is fine but will leave the created_at and updated_at columns blank.

2. The recommended Way

 public function up()
    {
        Schema::create('categories', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->timestamps();
        });


        $data =  array(
            [
                'name' => 'Category1',
            ],
            [
                'name' => 'Category2',
            ],
            [
                'name' => 'Category3',
            ],
        );
        foreach ($data as $datum){
            $category = new Category(); //The Category is the model for your migration
            $category->name =$datum['name'];
            $category->save();
        }

    }

A
Adam Hopkinson

This should do what you want.

public function up()
{
    DB::table('user')->insert(array('username'=>'dude', 'password'=>'z19pers!'));
}

p
protocod

Another clean way to do it is to define a private method which create instance et persist concerned Model.

public function up()
{
    Schema::create('roles', function (Blueprint $table) {
        $table->increments('id');
        $table->string('label', 256);
        $table->timestamps();
        $table->softDeletes();
    });

    $this->postCreate('admin', 'user');
}

private function postCreate(string ...$roles)  {
    foreach ($roles as $role) {
        $model = new Role();
        $model->setAttribute('label', $role);
        $model->save();
    }
}

With this solution, timestamps fields will be generated by Eloquent.

EDIT: it's better to use seeder system to disctinct database structure generation and database population.


I like this one... it servers exactly what I needed to do, add a few user roles in by default on migration. Need to make sure to either import the model or refer directly to it $model = new App\UserRoles();, but other than that... perfect!
A
Andrew Arscott

I tried this DB insert method, but as it does not use the model, it ignored a sluggable trait I had on the model. So, given the Model for this table exists, as soon as its migrated, I figured the model would be available to use to insert data. And I came up with this:

public function up() {
        Schema::create('parent_categories', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name');
            $table->string('slug');
            $table->timestamps();
        });
        ParentCategory::create(
            [
                'id' => 1,
                'name' => 'Occasions',
            ],
        );
    }

This worked correctly, and also took into account the sluggable trait on my Model to automatically generate a slug for this entry, and uses the timestamps too. NB. Adding the ID was no neccesary, however, I wanted specific IDs for my categories in this example. Tested working on Laravel 5.8


C
CodeToLife

If you already have filled columns and have added new one or you want to fill out old column with new mock values , do this:

public function up()
{
    DB::table('foydabars')->update(
        array(
            'status' => '0'
        )
    );
}

P
Paweł Moskal

By editing the first option from Martin Mbae's answer, if you want the same result as the second option but not using the model, you can just do this:

public function up()
{
    Schema::create('categories', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->timestamps();
    });

    $sampleData = ['sample1', 'sample2', 'sample3'];
    $data = [];

    foreach ($sampleData as $sample) {
        $data[] = [
            'table_name' => $sample,
            'created_at' => now(),
            'updated_at' => now()
        ];
    }
    
    DB::table('categories')->insert($data);
}

关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now