Laravel:生产数据的迁移和种子

gon*_*ard 39 php database database-migration laravel laravel-4

我的应用程序需要预先注册的数据集才能工作.所以我需要在设置应用程序时将它们插入数据库中.

Laravel提出了两种机制:

  • 数据库迁移:"它们允许团队修改数据库模式并在当前模式状态下保持最新."
  • 数据库播种:"Laravel还包括一种使用种子类为测试数据播种数据库的简单方法."

当我读到这个描述时,这些解决方案似乎都没有适应.

在stackoverflow上已经提出了类似的问题并回答了问题.答案建议使用数据库播种器通过检测当前环境来填充数据库:

<?php

class DatabaseSeeder extends Seeder {

    public function run()
    {
            Eloquent::unguard();

            if (App::environment() === 'production')
            {
                $this->call('ProductionSeeder');
            }
            else
            {
                $this->call('StagingSeeder');
            }
    }

}
Run Code Online (Sandbox Code Playgroud)

当然,这个解决方案有效.但我不确定这是否是正确的方法,因为通过使用播种机插入数据,您将失去迁移机制提供的所有优势(数据库升级,回滚......)

我想知道在这种情况下最佳做法是什么.

Ant*_*iro 62

Laravel的发展与自由有关.那么,如果您需要为生产数据库设定种子并认为DatabaseSeeder是最佳选择,那么为什么不呢?

好的,播种机主要用于测试数据,但你会发现有些人会像你一样使用它.

我将这种重要的种子视为迁移的一部分,因为这是我的数据库表之外的事情,并且artisan migrate每次部署新版本的应用程序时都会运行,所以我只是这样做

php artisan migrate:make seed_models_table
Run Code Online (Sandbox Code Playgroud)

并在其中创建我的seedind东西:

public function up()
{
    $models = array(
        array('name' => '...'),
    );

    DB::table('models')->insert($models);
}
Run Code Online (Sandbox Code Playgroud)

  • ✏️请注意,`migrate:make`不再定义.使用`make:migration`代替. (3认同)
  • 感谢您的回答安东尼奥。我将使用迁移来播种。 (2认同)
  • 这是一个很好的答案。迁移管理模式,但有时您需要移动数据。这一切都需要按照严格的顺序完成,并且迁移能够强制执行此顺序。 (2认同)

Dan*_*n B 30

我经常发现自己想知道对此的正确答案是什么.就个人而言,我没有使用种子来填充数据库中所需的行,因为你必须加载一些条件逻辑,以确保你不会尝试填充已经存在的东西.(删除和重新创建数据是非常不可取的,因为你可能最终导致密钥不匹配,如果你使用级联删除,你可能会意外擦除数据库的负载我的错误!;-)

我尽可能地将"播种"行放入迁移脚本中,数据将作为推出过程的一部分存在.

值得注意的是,您应该使用DB类而不是Eloquent模型来填充此数据,因为您的类结构可能会随着时间的推移而发生变化,这将阻止您从头开始重新创建数据库(无需重写历史记录并更改迁移文件,我确定这是件坏事.)

我倾向于使用这样的东西:

public function up()
{
    DB::beginTransaction();

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

    DB::table('town')
        ->insert(
            array(
                array('London'),
                array('Paris'),
                array('New York')
            )
        );

    Schema::create(
        'location',
        function (Blueprint $table) {
            $table->increments('id');
            $table->integer('town_id')->unsigned()->index();
            $table->float('lat');
            $table->float('long');
            $table->timestamps();

            $table->foreign('town_id')->references('id')->on('town')->onDelete('cascade');
        }
    );

    DB::commit();
}
Run Code Online (Sandbox Code Playgroud)

这样我就可以在第一次创建时轻松地"种子化"城镇表,并且不会干扰在运行时对其进行的任何添加.

  • “值得注意的是,您应该使用 DB 类而不是 Eloquent 模型”,这是一个非常好的观点,并且不仅适用于您的 Eloquent 模型,而且适用于任何特定于应用程序的代码!您的迁移将在新安装上使用最新代码运行,因此必须向前兼容。 (4认同)
  • 使用DB而不是Model的有趣点 (2认同)
  • 我知道这已经有一年多了,但是上面的内容真的有效吗?我想你会得到一个“非空违规”,因为你在插入表时没有包含“created_at”和“updated_at”字段。 (2认同)

Man*_*eet 14

This is what I use in production.

Since I run migration on each deployment

artisan migrate
Run Code Online (Sandbox Code Playgroud)

I create a seeder (just to keep seeding data out of migration for easy access later) and then run that seeder along with the migration

class YourTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {    
        //migrate your table // Example
        Schema::create('test_table', function(Blueprint $table)
        {
            $table->increments('id');
            $table->timestamps();
            $table->softDeletes();
        });

        //seed this table
        $seeder = new YourTableSeeder();
        $seeder->run();
    }

    /**
    * Reverse the migrations.
    *
    * @return void
    */
    public function down()
    {
        Schema::drop('test_table');
    }
}
Run Code Online (Sandbox Code Playgroud)

I do not add this seed call to seeds/DatabaseSeeder.php to avoid running it twice on a new installation.


小智 6

Artisan 命令解决方案

  1. 创建一个新的 artisan 命令

    php artisan make:command UpsertConfigurationTables

  2. 将其粘贴到新生成的文件中:UpsertConfigurationTables.php

    <?php
    
    namespace App\Console\Commands;
    
    use Exception;
    use Illuminate\Console\Command;
    
    class UpsertConfigurationTables extends Command
    {
        /**
         * The name and signature of the console command.
         *
         * @var string
         */
        protected $signature = 'upsert:configuration';
    
        /**
         * The console command description.
         *
         * @var string
         */
         protected $description = 'Upserts the configuration tables.';
    
        /**
         * The models we want to upsert configuration data for
         *
         * @var array
         */
        private $_models = [
            'App\ExampleModel'
        ];
    
    
        /**
         * Create a new command instance.
         *
         * @return void
         */
        public function __construct()
        {
            parent::__construct();
        }
    
        /**
         * Execute the console command.
         *
         * @return mixed
         */
        public function handle()
        {
            foreach ($this->_models as $model) {
    
                // check that class exists
                if (!class_exists($model)) {
                    throw new Exception('Configuration seed failed. Model does not exist.');
                }
    
                // check that seed data exists
                if (!defined($model . '::CONFIGURATION_DATA')) {
                    throw new Exception('Configuration seed failed. Data does not exist.');
                }
    
                /**
                 * seed each record
                 */
                foreach ($model::CONFIGURATION_DATA as $row) {
                    $record = $this->_getRecord($model, $row['id']);
                    foreach ($row as $key => $value) {
                        $this->_upsertRecord($record, $row);
                    }
                }
            }
        }
    
        /**
         * _fetchRecord - fetches a record if it exists, otherwise instantiates a new model
         *
         * @param string  $model - the model
         * @param integer $id    - the model ID
         *
         * @return object - model instantiation
         */
        private function _getRecord ($model, $id)
        {
            if ($this->_isSoftDeletable($model)) {
                $record = $model::withTrashed()->find($id);
            } else {
                $record = $model::find($id);
            }
            return $record ? $record : new $model;
        }
    
        /**
         * _upsertRecord - upsert a database record
         *
         * @param object $record - the record
         * @param array  $row    - the row of update data
         *
         * @return object
         */
        private function _upsertRecord ($record, $row)
        {
            foreach ($row as $key => $value) {
                if ($key === 'deleted_at' && $this->_isSoftDeletable($record)) {
                    if ($record->trashed() && !$value) {
                        $record->restore();
                    } else if (!$record->trashed() && $value) {
                        $record->delete();
                    }
                } else {
                    $record->$key = $value;
                }
            }
            return $record->save();
        }
    
        /**
         * _isSoftDeletable - Determines if a model is soft-deletable
         *
         * @param string $model - the model in question
         *
         * @return boolean
         */
        private function _isSoftDeletable ($model)
        {
            $uses = array_merge(class_uses($model), class_uses(get_parent_class($model)));
            return in_array('Illuminate\Database\Eloquent\SoftDeletes', $uses);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 填充$_models您想要播种的 Eloquent 模型。

  4. 定义模型中的种子行:const CONFIGURATION_DATA

    <?php
    
    namespace App;
    
    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Database\Eloquent\SoftDeletes;
    
    class ExampleModel extends Model
    {
        use SoftDeletes;
    
        const CONFIG_VALUE_ONE = 1;
        const CONFIG_VALUE_TWO = 2;
        const CONFIGURATION_DATA = [
            [
                'id'         => self::CONFIG_VALUE_ONE,
                'col1'       => 'val1',
                'col2'       => 'val2',
                'deleted_at' => false
            ],
            [
                'id'         => self::CONFIG_VALUE_TWO,
                'col1'       => 'val1',
                'col2'       => 'val2',
                'deleted_at' => true
            ],
        ];
    }
    
    Run Code Online (Sandbox Code Playgroud)
  5. 将命令添加到 Laravel Forge 部署脚本(或任何其他 CI 部署脚本)中:php artisan upsert:configuration

其他值得注意的事情:

  • 更新插入功能:如果您想要更改任何种子行,只需在模型中更新它们,下次部署时就会更新您的数据库值。它永远不会创建重复的行。
  • 软删除模型:请注意,您可以通过设置或 来deleted_at定义删除。Artisan 命令将处理调用正确的方法来删除或恢复您的记录。truefalse

其他提到的解决方案的问题:

  • 播种机:在生产中运行播种机是对播种机的滥用。我担心的是,未来的工程师会改变播种机,认为它是无害的,因为文档表明它们是为了播种测试数据而设计的。
  • 迁移:在迁移中播种数据很奇怪,并且滥用了迁移的目的。迁移运行后,它也不允许您更新这些值。