在Laravel迁移中如何使用SQLite更改主键

4
我有以下迁移方案:
Schema::create('items', function(Blueprint $table) {
    $table->uuid('id')->primary();
    // more columns ...
});

现在,我们想要添加一个额外的自增列:

Schema::table('items', function(Blueprint $table) {
    $table->dropPrimary('id');
    $table->rename('id', 'SystemId')->change();
    $table->id();
});

问题: SQLite不允许更改主键
解决方案: 建议删除表并使用更改后的模式重新创建

当然,理论上这样做可行,但复制第一次迁移的代码到第二次迁移中与DRY原则背道而驰。所以我的问题是:有没有其他方法可以实现这个目的?

2个回答

4

所以,我终于想出了一个足够通用可重用的解决方案。很希望这能被纳入Laravel,但更可能是做成一个包。

use Doctrine\DBAL\Schema\Table;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;

class ExtendedSQLiteAlterTableMigration extends Migration
{
    public function extendedAlterTable(string $tableName, callable $callback)
    {
        /** @var \Doctrine\DBAL\Schema\AbstractSchemaManager */
        $schemaManager = DB::connection()->getDoctrineSchemaManager();
        /** @var \Doctrine\DBAL\Schema\Table */
        $table = $this->getTempTable($schemaManager, $tableName);
        call_user_func($callback, $table);
        $tempName = $table->getName();
        //$schemaManager->renameTable($tableName, $tempName);
        $schemaManager->createTable($table);
        $schemaManager->dropTable($tableName);
        $schemaManager->renameTable($tempName, $tableName);
    }

    private function getTempTable($schemaManager, string $name)
    {        
        $columns     = $schemaManager->listTableColumns($name);
        $foreignKeys = [];

        //if ($this->_platform->supportsForeignKeyConstraints()) {
            $foreignKeys = $schemaManager->listTableForeignKeys($name);
        //}

        $indexes = $schemaManager->listTableIndexes($name);

        return new Table("temp_$name", $columns, $indexes, [], $foreignKeys);
    }
}

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class AddAutoIncrementPrimaryKeyToTestTable extends ExtendedSQLiteAlterTableMigration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        $this->extendedAlterTable('test', function(Table $table) {$table->dropPrimaryKey();
            $table->addColumn('id', 'bigint', [
                'autoincrement' => true,
            ]);
            $table->setPrimaryKey([ 'id' ]);
        });
    }
}

这遵循了SQLite官网上有关修改表结构的其他指南

0

在创建SQLite表之后,您无法以任何重要的方式修改它们。正如您所说,接受建议的解决方案是创建一个具有正确要求的新表,并将数据复制到其中,然后删除旧表。这是唯一的方法。

关于此的官方文档:http://sqlite.org/faq.html#q11


谢谢您的回答。我知道如何在原始SQL中完成这个操作。我的问题是如何在Laravel中以一种非重复的方式实现它。 - shaedrich
这是我个人的做法:将旧迁移的内容复制并粘贴到新迁移中。按照您的意愿进行更改,然后删除旧迁移。接着继续从当前表导出数据,然后删除它。进入数据库中的 migrations 表并从中删除迁移,以便 Laravel 知道它没有被迁移。然后运行 php artisan migrate 并在 SQL 文件中将 id 替换为 SystemId 后导入数据。在您的模型中声明新的主键 protected $primaryKey = 'SystemId';,我想您就完成了。 - Laralex
这听起来并不是一种完全可靠的方法,因为涉及到许多手动步骤。此外,需要考虑到这些迁移可能已经被其他人运行过,他们也需要相应地进行更改。如果该表有多个迁移,您需要为每个迁移单独执行这些步骤。 - shaedrich
数据库语法和平台实现使用TableDiff,因此它可以部分自动化。但是我觉得我仍然需要自己编写其余部分,或者我错过了任何一种已经自动化删除旧表并创建新表的方法吗? - shaedrich
我发布了一个回答,说明了我所做的事情。我希望它更符合DRY原则,但至少有一些东西比手动全部完成更符合DRY原则。 - shaedrich
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接