10

希望我能很好地解释这一点。

我有一个已经投入生产一分钟的 Laravel 应用程序。所以,我有一堆迁移文件有很多变化。我想在不丢失数据库的情况下合并这些迁移文件。

认为这会起作用的方式:

  1. 将所有生产表迁移到所需状态。
  2. 将所有迁移文件合并到所需的最少文件数中。
  3. 清除迁移表。
  4. 运行迁移或填充迁移表。

我想这样做的部分原因是因为我想以尽可能干净的迁移集公开一些服务提供商。

困难的版本可能是:

  1. 备份或复制表。
  2. 运行迁移。
  3. 编写并运行脚本来填充“干净”的表。

只是希望有比这更简单的方法。

编辑(来自评论):我有一个生产数据库,其中包含大约 50 多个迁移文件 - 一些小改动,一些大改动。如果我合并,需要的迁移数量大约为 12 次左右。我想整合迁移文件,但仍然能够migrate:rollback在生产环境中执行 - 我不会。

4

4 回答 4

11

经过几次过度设计和过于聪明的解决方案尝试后,我认为以下是该问题的可行解决方案。

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会收到错误消息,指出无法找到迁移。

注意事项

  1. 如果架构在设计上不是模块化的,那么它可能会非常麻烦。但是,如果您已将代码分成服务,它似乎会更容易一些。
  2. 迁移期间的 Laravel 错误处理和消息非常有限;因此,调试可能很困难。
  3. 强烈建议从您的应用程序/服务中最稳定的表开始。此外,从您的应用程序的基础开始,也可能证明是有益的。

注意:当我在生产中实际这样做时,而不仅仅是我的本地(一遍又一遍),如果没有更好的答案,那么我会接受这个。

注意事项

/config/app如果您通过谨慎的迁移将应用程序分解为服务提供者,那么您可以在运行迁移时注释掉服务提供者。这样,您就可以为现在的基线服务创建一个批处理。因此,假设您有以下迁移,其中每个字母代表一个迁移,每个重复的字母代表相同的服务:

  • 一个
  • C
  • 一个
  • C
  • 一个

合并服务 A 后:

  • C
  • C
  • 一个

合并 B 后:

  • C
  • C
  • 一个

合并 C 后:

  • 一个
  • C

更新

到目前为止,从 54 次迁移到 27 次。我什至从大型up()down()方法中提取了一些 Schema 更改,并将它们分开迁移。这里很好的副作用是批次。我从支持其他所有内容的基表开始迁移;因此,回滚更多的是服务。

于 2017-09-07T00:59:30.497 回答
5

您可以使用库 " xethron/migrations-generator" 。这是回购

安装后,基本用法: php artisan migrate:generate

在 laracast上可以找到一些关于此的讨论。

于 2018-02-15T08:55:23.470 回答
1

看来你做错了。通常你不应该碰你的迁移,所以没有必要合并它们。特别是如果您将代码推送到生产服务器中,则根本不应该触摸。

总而言之 - 迁移是为了帮助您保持数据库架构应有的状态,但是如果您想使用它们,合并它们或重建它们,没有人会阻止您,但是如果您犯了错误,您可以轻松地破坏某些东西。

进行多次迁移并进行少量更改并没有错 - 这就是它的工作方式,您对 PHP 文件进行更改,您在架构中进行更改。

于 2017-09-05T14:51:49.120 回答
0

我在寻找有关组合(laravel 称之为 squashing)迁移的信息时发现了这个问题。对于将来寻找此信息的任何人,我想我会放弃答案。

Laravel 8.0 现在允许这样做:

./artisan schema:dump同时使用您的数据库 cli 客户端将数据库模式转储导出到 laravel 的数据库/模式文件夹中。

如果没有在目标数据库上运行迁移,未来的调用./artisan migrate将首先执行模式文件,然后它将在存储在模式转储文件中的最后一个迁移之后拾取并运行所有迁移。

你可以在这里阅读更多Laravel 8.0 Squash 数据库迁移

于 2021-07-12T04:00:20.857 回答