问题描述:

I'm trying to figure out how to deal with structural as well as data concerned changes within migrations in Laravel 5.2.

At the moment I just change the existing data after applying structural changes to the database.

But that seems to collide with the version controlled code.

A simple example

A column owner_id should be added to the teams table. Simple migration, no problem. Afterwards, all existing teams should get such an id. Therefore I put this in the migration, too:

foreach ($teams as $team) {

// get the first user of this team over a many-to-many-relationship-table `team_user`

$team->owner_id = $team->users->first()->id;

...

}

At the first glance that works fine.

After a few days and commits, I decide to add a pivot column role in the mentioned relation table. I add the migration and in the team model I put something like this:

public function users() {

return $this->belongsToMany(...)->withPivot('role');

}

The resulting problem

If I want to completely refresh my local database now, I'll get an error in the first migration. That's because it called $team->users, but in this state of the database the role isn't available yet and so the method call fails. It's like a shift between version control and database migrations.

Is there a clean solution for this?

Big thanks in advance,

Richard

网友答案:

Usually migrating is DDL (data definition language) and seeding is DML (data manipulation language).

Migrating database tables is a matter of order. You cannot add code which depends on some DDL, which may be executed before those DDL are run.

Another thing you have to consider is: migrations are not the place to do data seeding, modification, manipulation, because seeding may require ALL data definition is done before you start seeding. That's why Laravel has data seeding.

So, there is no difference between seeding, fixing nor changing data, it's all DML. You can do it in your migrations, it's not forbidden, but in those you have to be very careful by undoing (on down) every single step you took in the up method. But sometimes it is too hard to manipulate back your data.

Your problem is that you are trying to access some data, via relations, which is not ready (or done, or seeded) yet, because a migration which would do that were not run yet. So you have a conflict between DDL and DML, which would not occur if you treated them separately.

Usually data seeding is done only for testing, but if you really need it you can do something like this in your seeder:

<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        if (app()->environment('testing')) {
            $this->call(TestingSeeders::class);
        }

        $this->call(ProductionReadySeeders::class);
    }
}

Or you can just create a command to seed your data:

Artisan::command('app:seed', function ($email)
{
    // Do whatever you need to seed your database tables
});

And run with

php artisan app:seed

If you want to make sure everyone who gets a new version if your app also get the correct data, you can use Composer post execution commands to do so:

"post-install-cmd": [
   "Illuminate\\Foundation\\ComposerScripts::postInstall",
   "php artisan optimize",
   "php artisan migrate --force",
   "php artisan app:seed" 
],

"post-update-cmd": [
   "Illuminate\\Foundation\\ComposerScripts::postUpdate",
     "php artisan optimize",
     "php artisan migrate --force", 
     "php artisan app:seed"
]
相关阅读:
Top