经过几次过度设计和过于聪明的解决方案尝试后,我认为以下是该问题的可行解决方案。
tl;dr:
- 迁移两侧的 Bookend 迁移从无到有构建架构。
- 更新项目。
- 迁移。
- 删除书挡和所有以前的迁移。
- 删除记录来自
migrations
table.
第一个书挡重命名受影响的表。第二个书挡将数据从重命名的表复制到新表,然后删除重命名的表。
注意:您可以在书挡内做任何您喜欢的事情,这只是最低要求。
因此,假设您的迁移类似于以下内容:
- 2017_09_05_000000_create_some_table.php
- 2017_09_05_000001_add_field_x_to_some_table.php
- 2017_09_05_000002_add_field_y_to_some_table.php
- 2017_09_05_000003_add_field_z_to_some_table.php
我们将创建另一个迁移:
- 2017_09_05_000004_pre_refresh.php
我们将根据我们现有的知识创建另一个迁移:
- 2017_09_05_000005_create_some_table.php
我们将创建最后一个书挡,其中将发生数据迁移:
- 2017_09_05_000006_post_refresh.php
前四个迁移将不会运行,因为它们已经运行过。
/** 2017_09_05_000004_pre_refresh.php */
class PreRefresh extends Migration
{
public function up()
{
$prefix = 'zz_';
$tablesToRename = [
'foos',
'bars'
];
foreach($tablesToRename as $table) {
Schema::rename($table, $prefix . $table);
}
}
}
无需降价,因为这是一次性交易。这将首先运行,这将导致数组中列出的所有表被重命名。然后将运行合并的(优化的)迁移。
/** 2017_09_05_000006_post_refresh.php */
class PostRefresh extends Migration
{
public function up()
{
// Do what you need to do.
// If you cannot use your models, just use DB::table() commands.
$foos = DB::table('zz_foos')->get();
foreach ($foos as $foo) {
DB::table('foo')->insert([
'id' => $foo->id,
'created_at' => $foo->created_at,
'updated_at' => $foo->updated_at
]);
}
$bars = DB::table('zz_bars')->get();
foreach ($bars as $bar) {
DB::table('bar')->insert([
'id' => $bar->id,
'created_at' => $bar->created_at,
'updated_at' => $bar->updated_at,
'foo_id' => $bar->foo_id
]);
}
// Tear down.
$prefix = 'zz_';
$tablesToRename = [
'foo',
'bar'
];
foreach ($tablesToRename as $table) {
DB::statement('SET FOREIGN_KEY_CHECKS=0');
Schema::dropIfExists($prefix . $table);
DB::statement('SET FOREIGN_KEY_CHECKS=1');
}
}
}
运行此命令后,您可以从pre_refresh
和之前。以及post_refresh
。然后你可以进入migrations
表并删除这些迁移的条目。
删除条目并不是完全必要的,但如果您migrate:rollback
您将收到错误消息,指出无法找到迁移。
Caveats
- 如果架构在设计上不是模块化的,那么它可能会非常麻烦。但是,如果您将代码分离到服务中,那么看起来确实会容易一些。
- Laravel 迁移过程中的错误处理和消息非常有限;因此,调试可能会很困难。
- 强烈建议从应用程序/服务中最稳定的表开始。此外,从应用程序的基础内容开始也可能是有益的。
注意:当我实际在生产中这样做时,而不仅仅是在我的本地(一遍又一遍),如果没有更好的答案,那么我会接受这一点。
注意事项
如果您通过谨慎的迁移将应用程序分解为服务提供者,那么您可以在/config/app
当您运行迁移时。通过这种方式,您可以为现已基线化的服务创建一个批次。因此,假设您有以下迁移,其中每个字母代表一个迁移,每个重复的字母代表相同的服务:
合并服务A后:
合并B后:
合并后C:
update
到目前为止,迁移次数已从 54 次减少到 27 次。我什至从大型中提取了一些架构更改up()
and down()
方法并使它们单独迁移。这里的一个很好的副作用是批次。我从支持其他所有内容的基表开始进行迁移;因此,回滚是更多的服务。