我的应用程序需要预先注册的数据集才能工作。因此,当我设置应用程序时,我需要将它们插入数据库中。
Laravel提出两种机制:
当我阅读此描述时,这些解决方案似乎都不适合。
类似的问题已在 stackoverflow 上被问及并得到解答。答案建议使用数据库播种器通过检测当前环境来填充数据库:
<?php
class DatabaseSeeder extends Seeder {
public function run()
{
Eloquent::unguard();
if (App::environment() === 'production')
{
$this->call('ProductionSeeder');
}
else
{
$this->call('StagingSeeder');
}
}
}
当然,这个解决方案是有效的。但我不确定这是正确的方法,因为通过使用播种器插入数据,您将失去迁移机制提供的所有优势(数据库升级、回滚...)
我想知道这种情况下的最佳做法是什么。
好吧,播种机主要用于测试数据,但你会看到有些人像你一样使用它。
我将这种重要的种子视为我的迁移的一部分,因为这是不能从我的数据库表中删除的东西,并且每次部署应用程序的新版本时都会运行
artisan migrate
,所以我就这么做了
php artisan make:migration seed_models_table
并在其中创建我的种子内容:
public function up()
{
$models = array(
array('name' => '...'),
);
DB::table('models')->insert($models);
}
不可取的,因为您可能会导致键不匹配,并且如果您使用级联删除,您可能会意外地错误地擦除数据库的负载!;-) 我将行的“播种”放入迁移脚本中,因为很有可能,数据需要作为部署过程的一部分存在。
值得注意的是,您应该使用 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();
}
这样我就可以在第一次创建城镇表时轻松地“播种”城镇表,并且不会干扰运行时对其进行的任何添加。
因为我在每个部署上运行迁移
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 以避免在新安装上运行它两次。
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
],
];
}
php artisan upsert:configuration
deleted_at
设置为 true
或 false
来定义删除。 Artisan 命令将处理调用正确的方法来删除或恢复您的记录。
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');
}