经过几次过度设计和过于聪明的解决方案尝试,我认为以下是该问题的可行解决方案。
tl;博士:
- 从无到有构建架构的迁移两侧的书挡迁移。
- 更新项目。
- 迁移。
- 删除书挡和所有以前的迁移。
- 从
migrations 表中删除记录。
第一个书挡重命名受影响的表。第二个书挡将数据从重命名的表复制到新表,然后删除重命名的表。
注意:你可以在书挡里做任何你想做的事,这只是最低限度。
所以,让我们假设您的迁移如下所示:
- 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,您将收到错误消息,指出找不到迁移。
注意事项
- 如果架构在设计上不是模块化的,那么它可能会非常麻烦。但是,如果您已将代码分成服务,它似乎会更容易一些。
- 迁移期间的 Laravel 错误处理和消息非常有限;因此,调试可能会很困难。
- 强烈建议从应用/服务中最稳定的表开始。此外,从那些对您的应用至关重要的内容开始也可能会证明是有益的。
注意:当我实际在生产中这样做时,而不仅仅是我的本地(一遍又一遍),如果没有更好的答案,那么我会接受这个。
注意事项
如果您通过谨慎的迁移将您的应用程序分解为服务提供者,那么您可以在运行迁移时注释掉/config/app 中的服务提供者。这样,您就可以为现在的基线服务创建一个批处理。因此,假设您有以下迁移,其中每个字母代表一个迁移,每个重复的字母代表相同的服务:
合并服务 A 后:
合并B后:
合并C后:
更新
到目前为止,从 54 次迁移到 27 次。我什至从大型 up() 和 down() 方法中提取了一些 Schema 更改,并将它们单独迁移。这里很好的副作用是批次。我从支持其他所有内容的基表开始迁移;因此,回滚更多的是服务。