我的应用程序需要预先注册的数据集才能工作。所以我需要在设置应用程序时将它们插入数据库中。
Laravel提出了两种机制:
当我读到这个描述时,这些解决方案似乎都没有被改编。
类似的问题是asked on stackoverflow和answered。答案建议使用数据库播种器通过检测当前环境来填充数据库:
<?php
class DatabaseSeeder extends Seeder {
public function run()
{
Eloquent::unguard();
if (App::environment() === 'production')
{
$this->call('ProductionSeeder');
}
else
{
$this->call('StagingSeeder');
}
}
}
当然,这个解决方案有效。但我不确定这是否是正确的方法,因为通过使用播种机插入数据,您将失去迁移机制提供的所有优势(数据库升级,回滚......)
我想知道在这种情况下最佳做法是什么。
答案 0 :(得分:61)
Laravel的发展与自由有关。那么,如果您需要为生产数据库设定种子并认为DatabaseSeeder是最佳选择,那么为什么不呢?
好的,播种机主要用于测试数据,但你会看到一些人在使用它。
我将这种重要的种子视为迁移的一部分,因为这是我的数据库表之外的事情,每次部署新版本的应用程序时都会运行artisan migrate
,所以我只是这样做
php artisan migrate:make seed_models_table
并在其中创建我的种子:
public function up()
{
$models = array(
array('name' => '...'),
);
DB::table('models')->insert($models);
}
答案 1 :(得分:29)
我经常发现自己想知道对此的正确答案是什么。就个人而言,我没有避免使用种子来填充数据库中所需的行,因为您必须加载一些条件逻辑以确保您不会尝试填充“#39”中的内容。已经存在了。 (删除和重新创建数据非常是不可取的,因为您最终可能会出现密钥不匹配,如果您使用级联删除,则可能会意外擦除数据库的负载!! - )< / p>
我把'播种'&#39;如果有机会进入迁移脚本,那么数据将需要作为推出过程的一部分存在。
值得注意的是,您应该使用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();
}
然后这让我可以播种#39;我第一次创建它时很容易在城镇表中使用,并且不会干扰在运行时对其进行的任何添加。
答案 2 :(得分:3)
Artisan Command Solution
创建新的工匠命令
php artisan make:command UpsertConfigurationTables
将其粘贴到新生成的文件中: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);
}
}
用您想要播种的雄辩模型填充$_models
。
定义模型中的种子行: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
],
];
}
将命令添加到您的Laravel Forge部署脚本(或任何其他CI部署脚本):php artisan upsert:configuration
其他值得注意的事情:
deleted_at
设置为true
或false
来定义删除。 Artisan命令将处理调用正确的方法以删除或恢复您的记录。其他提及的解决方案的问题:
答案 3 :(得分:1)
这是我在生产中使用的。
自从我在每个部署上运行迁移
artisan migrate
我使用以下方法创建了一个种子器(只是为了防止在迁移过程中保留种子数据,以便以后访问,并仅查看种子目录中的文件就知道了我播种了哪些数据)
php artisan make:seeder YourTableSeeder
,然后在迁移中调用它。
class YourTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$seeder = new YourTableSeeder();
$seeder->run();
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
}
}
我不会将此种子调用添加到seed / DatabaseSeeder.php中,以避免在新安装中运行两次。