如何在laravel迁移中更改枚举类型列?

55

我正在使用Laravel 5.1,我有一个名为packages的表,它的结构如下:

id              int(11)
weight          decimal(10,2)           
weight_unit     enum('Kg.', 'Gm.')

我想要将 weight_unit 枚举类型更改为:

weight_unit enum('克','千克','磅')

为此,我创建了以下迁移:

public function up()
{
    Schema::table('packages', function ($table) {
        $table->enum('weight_unit', array('Grams','Kgs.','Pounds'))->nullable()->change();
    });
}

但是当我运行迁移时,我收到一个错误:

Unknown database type enum requested, Doctrine\DBAL\Platforms\MySqlPlatform  

may not support it.

我该如何更改这个枚举?

10个回答

67

使用 DB::statement 方法:

DB::statement("ALTER TABLE packages MODIFY COLUMN weight_unit ENUM('Grams', 'Kgs', 'Pounds')");

5
请注意,使用此解决方案时,您只能使用支持ENUM类型的数据库。例如,尝试在SQLite上运行测试会导致错误。如果不需要跨平台移植,并且始终使用MySQL,则可以正常工作。 - Paul Redmond
7
你的语法错误了。如果你不想重复列名,就需要使用 MODIFY 而不是 CHANGE - mpen
1
Laravel 迁移具有一个抽象层,这使得它适用于 MySQL,但不适用于 PostgresSQL。对于后者,它会模拟枚举为带约束的 varchar(不知道为什么,因为 postgres 支持枚举)。这就是如果你想要与 Laravel 迁移创建的枚举列相同的结果。 - challet
我已根据@mpen的评论修复了语法。 - Synchro
4
SQLite 不支持 Statement,因此 PHPUnit 测试将失败! - Solomon Antoine

27

当我向修改后的枚举列添加新的枚举值时,这对我起了作用。

up() 方法中添加以下内容:

DB::statement("ALTER TABLE packages MODIFY weight_unit ENUM('Grams', 'Kgs', 'Pounds', 'new value') NOT NULL");

然后在down()方法中,您可以恢复所做的更改:

DB::statement("ALTER TABLE packages MODIFY weight_unit ENUM('Grams', 'Kgs', 'Pounds') NOT NULL");

注意:在删除枚举值之前,需要将其更改为另一个将保留的枚举值。


13
$table->enum('level', ['easy', 'hard']);

Ram,完整示例迁移:使用Illuminate\Database\Migrations\Migration; 使用Illuminate\Database\Schema\Blueprint;class CreateCampaignConversionTable extends Migration { public function up() { Schema::create('campaign_conversion', function(Blueprint $table) { $table->increments('id'); $table->enum('status', array( 'pending', 'completed', 'failed', 'expired' ))->nullable()->index('status'); $table->timestamps(); }); } } - Pablo Ramires
2
这将为列创建一个枚举。但无法用于修改现有列的枚举值。 - Jiechao Wang

10

我认为通过添加对原生列修改的支持,Laravel 10 已经修复了这个问题。

https://github.com/laravel/framework/pull/45487

所以从 Laravel 10 开始,你可以这样做:

Schema::table('jobs', function (Blueprint $table) {
    $table->enum('type', ['contract', 'permanent', 'partial'])->change();
});

我在全新的 Laravel 9.55.0 和 10.0.2 应用程序上尝试了相同的迁移。

laravel-9.52.0.jpg

laravel-10.0.2.jpg

更新: 如果您在项目中安装了“doctrine/dbal”,请在您的App\Providers\AppServiceProvider类的boot方法中或者在您的迁移文件中调用Schema::useNativeSchemaOperationsIfPossible()方法,以便能够使用原生模式操作,就像在上面的GitHub拉取请求中提到的那样。

1
就我所知,它似乎无法在运行10.8.3-MariaDB Homebrew的Mac上与Laravel 10.1.5一起使用... - William Turrell
@WilliamTurrell,我刚刚使用10.11.2-MariaDB测试过了,它能够正常工作。https://imgur.com/a/08NCPrM - Igor

5

如果您不想丢失数据,并要使用我提供的新值进行更新,则可以使用以下解决方案:

// Include old and new enum values
DB::statement("ALTER TABLE packages MODIFY COLUMN weight_unit ENUM('Kg.', 'Gm.', 'Grams', 'Kgs', 'Pounds')");
// Replace Kg. with Kgs
Packages::where('weight_unit', 'Kg.')->update(['weight_unit' => 'Kgs']);
// Replace Gm. with Grams
Packages::where('weight_unit', 'Gm.')->update(['weight_unit' => 'Grams']);
// Delete old values
DB::statement("ALTER TABLE packages MODIFY COLUMN weight_unit ENUM('Grams', 'Kgs', 'Pounds')");

这样,您就可以用新值替换旧值。

在迁移文件中使用模型类是一种不好的做法,因为存在属性不同步等风险。应该使用DB::table('packages')->where(...)->update(...);来代替。 - Sergey Nevmerzhitsky

4
您可以在迁移中添加自定义构造函数,并向Doctrine解释应将枚举视为字符串。
public function __construct(\Doctrine\DBAL\Migrations\Version $version)
{
    parent::__construct($version);

    $this->platform->registerDoctrineTypeMapping('enum', 'string');
}

5
我无法理解你希望这个类属于哪个类别。你说是在迁移中,所以我认为你指的是继承Migration的类。但我收到了错误信息“__construct()必须是Doctrine\DBAL\Migrations\Version的实例,没有给出任何实例,在/home/vagrant/asb/vendor/laravel/framework/src/Illuminate/Database/Migrations/Migrator.php的335行调用”,而且我看不到\Doctrine\DBAL\Migrations\Version命名空间,但我看到了Doctrine\DBAL\VersionDoctrine\Common\Version - Jonathan
Jonathan,你很可能缺少doctrine/dbal包。 - Robin De Schepper
2
此外,如果此代码属于迁移构造函数,则在较新的Laravel版本中将更好地发挥作用:DB :: getDoctrineSchemaManager()-> getDatabasePlatform()-> registerDoctrineTypeMapping('enum','string'); 构造函数还需要0个参数。 - Robin De Schepper

4

在调用change()之前添加以下内容:

DB::getDoctrineSchemaManager()->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string');

为什么这不是 Laravel 迁移中的“默认指令”?我已经卡了几个小时,一直在寻找错误...谢谢! - Patrick Maciel

1

使用默认值,在up()中添加此代码:

\DB::statement("ALTER TABLE `patient_appointments` CHANGE `status` `status` ENUM('pending','wait','approved', 'consulted') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'pending';");

1

我可以通过删除和添加约束来解决这个问题。这样做可以确保我的现有数据也是完好无损的。

DB::statement("ALTER TABLE purchases DROP CONSTRAINT purchases_ref_check");
$types = ['single', 'monthly', 'biannual', 'amount', 'other'];
$result = join( ', ', array_map(function( $value ){ return sprintf("'%s'::character varying", $value); }, $types) );
DB::statement("ALTER TABLE purchases add CONSTRAINT purchases_ref_check CHECK (ref::text = ANY (ARRAY[$result]::text[]))");

这是PostgreSQL案例的唯一有效解决方案。 - Sergey Nevmerzhitsky

-1
在Postgres中,我没有找到更优雅的解决方案,但是有一个例子:
public function up()
{
    if (Schema::hasColumn('plan_fibras', 'type')) {
        Schema::table('plan_fibras', function (Blueprint $table)
        {
            $table->dropColumn('type');
        });
    }

    if (!Schema::hasColumn('plan_fibras', 'type')) {
        Schema::table('plan_fibras', function (Blueprint $table) {
            $table->enum('type', ['PF', 'PJ', 'FIBRA X'])->nullable();
        });
    }
}

/**
 * Reverse the migrations.
 *
 * @return void
 */
public function down()
{
    if (Schema::hasColumn('plan_fibras', 'type')) {
        Schema::table('plan_fibras', function (Blueprint $table)
        {
            $table->dropColumn('type');
        });
    }
}

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