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

问题描述 投票:0回答:5

我的应用程序需要预先注册的数据集才能工作。因此,当我设置应用程序时,我需要将它们插入数据库中。

Laravel提出两种机制:

  • 数据库迁移“它们允许团队修改数据库架构并保持最新的当前架构状态。”
  • 数据库播种“Laravel 还提供了一种使用种子类为数据库添加测试数据的简单方法。”

当我阅读此描述时,这些解决方案似乎都不适合。

类似的问题已在 stackoverflow 上被问及并得到解答。答案建议使用数据库播种器通过检测当前环境来填充数据库: <?php class DatabaseSeeder extends Seeder { public function run() { Eloquent::unguard(); if (App::environment() === 'production') { $this->call('ProductionSeeder'); } else { $this->call('StagingSeeder'); } } }

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

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

php database laravel laravel-4 database-migration
5个回答
82
投票

好吧,播种机主要用于测试数据,但你会看到有些人像你一样使用它。

我将这种重要的种子视为我的迁移的一部分,因为这是不能从我的数据库表中删除的东西,并且每次部署应用程序的新版本时都会运行

artisan migrate

,所以我就这么做了

php artisan make:migration seed_models_table

并在其中创建我的种子内容:

public function up() { $models = array( array('name' => '...'), ); DB::table('models')->insert($models); }



43
投票
非常

不可取的,因为您可能会导致键不匹配,并且如果您使用级联删除,您可能会意外地错误地擦除数据库的负载!;-) 我将行的“播种”放入迁移脚本中,因为很有可能,数据需要作为部署过程的一部分存在。

值得注意的是,您应该使用 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(); }

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


22
投票

因为我在每个部署上运行迁移

artisan migrate

我创建一个播种器(只是为了将播种数据保留在迁移之外,以便以后轻松访问),然后在迁移的同时运行该播种器

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'); } }

我不会将此种子调用添加到 seeds/DatabaseSeeder.php 以避免在新安装上运行它两次。


6
投票

    创建一个新的 artisan 命令
  1. php artisan make:command UpsertConfigurationTables

    
    

  2. 将其粘贴到新生成的文件中:
  3. 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);
        }
    }
    

  4. 使用您想要种子的 Eloquent 模型填充
  5. $_models

    
    

  6. 定义模型中的种子行:
  7. 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
            ],
        ];
    }
    

  8. 将命令添加到 Laravel Forge 部署脚本(或任何其他 CI 部署脚本)中:
  9. php artisan upsert:configuration

    
    

  10. 其他值得注意的事情:

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

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

0
投票

use Illuminate\Support\Facades\Artisan; return new class extends Migration { /** * Run the migrations. */ public function up(): void { Schema::create('roles', function (Blueprint $table) { $table->id(); }); Artisan::call('db:seed --class=DataSeeder --force'); }

© www.soinside.com 2019 - 2024. All rights reserved.