Laravel 5.1请求未知的数据库类型枚举

93

在运行 php artisan migrate 时,我遇到了以下错误:

[Doctrine\DBAL\DBALException]
请求未知的数据库类型 enum ,Doctrine\DBAL\Platforms\MySqlPlatform 可能不支持它。

如何解决这个问题。

代码:

public function up() {
    Schema::table('blogs', function (Blueprint $table) {
        $table->string('wordpress_id')->nullable();
        $table->string('google_blog_id')->nullable()->change();
    });
}

请发布您尝试迁移的架构。 - Adrenaxus
公共函数up() { Schema::table('blogs', function (Blueprint $table) { $table->string('wordpress_id')->nullable(); $table->string('google_blog_id')->nullable()->change(); }); } - karthick
你的表中是否包含一个 enum 列? - Adrenaxus
在 Laravel 中,不支持重命名带有枚举列的表中的列。无论您是否尝试更改其他列,如果表包含“枚举”,它都无法正常工作。恐怕您必须手动进行更改,或尝试一些相当丑陋的黑客或解决方法。 - Adrenaxus
你能提供我需要遵循的步骤吗? - karthick
显示剩余3条评论
10个回答

150

正如Laravel 5.1的文档所述,这是一个已知问题。

注意:不支持在具有enum列的表中重命名列。

如果您的数据库表中有enum列,则无论您试图将另一列重命名为该列,还是将另一列更改为nullable,都会出现此错误。这是Doctrine\DBAL的问题。

解决此问题的简单方法是在数据库迁移文件中添加此构造函数方法。

public function __construct()
{
    DB::getDoctrineSchemaManager()->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string');
}

这将把所有ENUM列映射为VARCHAR(),并且该列将接受任何字符串。

这在Laravel 5.1和Laravel 5.3中对我有效。我希望这个错误能够很快得到修复。

感谢@Gmatkowski在 https://dev59.com/214b5IYBdhLWcg3wbA-B#32860409 的答案。


16
应该接受这个答案!谢谢@Xeleon - Ema4rl
6
这个更改会全局改变类型映射,还是只针对指定的迁移? 如果是全局的话,会有其他副作用吗? 为确保我使用了旧的 Laravel 4 方法,这种方法也可以运行(http://laraveldaily.com/schema-builder-changing-table-columns-only-laravel-5-0/)。 - Bas Weitjens
4
为了使它正常工作,我不得不在 AppServiceProvider::boot() 中添加那一行。 - andreshg112
谢谢,那个方法可行。我在6.x版本中遇到了这个问题,现在看来这个已知问题在8.x版本中仍然存在。 - chrisan

128

官方的Laravel 5.1文档指出:

注意:当前不支持在包含enum列的表中重命名列。

如果表中包含enum列,无论您试图更改另一列,都不会起作用。这是一个Doctrine DBAL问题。

作为解决方法,您可以删除该列并添加新列(列数据将丢失):

public function up()
{
    Schema::table('users', function(Blueprint $table)
    {
        $table->dropColumn('name');
    });

    Schema::table('users', function(Blueprint $table)
    {
        $table->text('username');
    });
}

或使用数据库语句:

public function up()
{
    DB::statement('ALTER TABLE projects CHANGE slug url VARCHAR(200)');
}

public function down()
{
    DB::statement('ALTER TABLE projects CHANGE url slug VARCHAR(200)');
}

来源:https://github.com/laravel/framework/issues/1186


2
感谢您提供的解决方案,删除列并不总是可行的,因此手动修改应该可以解决。 - Ali Alwash
4
如果您没有使用 $table,则不需要在 DB::statement 周围添加 Schema - Jonathan
1
真是太遗憾了!我试图更新与枚举无关的列,但这却导致了异常。好吧,我想DB::statement在这里是最佳选择。谢谢。 - MarkSkayff
1
我更喜欢第二种方法,因为第一种方法会影响数据,因为它会删除并重新创建列。但第二种方法只会影响表结构。 - Gayan
4
doctrine/dbal有什么问题,导致他们不能让你执行一个非常容易用SQL查询完成的操作? - Spencer Williams
1
不要使用“CHANGE”,而是使用“MODIFY COLUMN”进行替换,效果非常好! - Lizesh Shakya

9
我通过创建一个新的迁移类,并使我的迁移继承自它来解决了这个问题。也许有多种方法可以使其更加“标准”,但这只是一个非常简单的情况,完美地适用于我们的团队。
use Doctrine\DBAL\Types\{StringType, Type};
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\{DB, Log};

/**
 * Class ExtendedMigration
 * Use it when the involved table(s) has enum type column(s)
 */
class ExtendedMigration extends Migration
{
    /**
     * ExtendedMigration constructor.
     * Handle Laravel Issue related with modifying tables with enum columns
     */
    public function __construct()
    {
        try {
            Type::hasType('enum') ?: Type::addType('enum', StringType::class);
            Type::hasType('timestamp') ?: Type::addType('timestamp', DateTimeType::class);
        } catch (\Exception $exception) {
            Log::info($exception->getMessage());
        }
    }
}

然后,就像之前解释的那样,只需扩展您的迁移。
class SampleMigration extends ExtendedMigration
{
    public function up()
    {
        Schema::create('invitations', function (Blueprint $table) {
            ...
            $table->enum('status', ['sent', 'consumed', 'expired'])->default('sent');
            ...
        });
    }

    public function down()
    {
        Schema::dropIfExists('invitations');
    }
}

把这个ExtendedMigration放在哪里?我成功地直接将类型添加到了普通迁移的up函数中,但我想知道你把ExtendedMigration类放在哪里。 - Dika
1
@Dika,根据你的项目类型,如果你正在构建一个库,把它放在数据库文件夹中可能是有意义的。在我的情况下,我们有一个常规的Laravel项目,我把它放在helpers文件夹下app/Helpers/ExtendedMigration.php,当然,它的命名空间是App\Helpers - Hector Manuel

6

Laravel: 5.8、6、7、8

use Doctrine\DBAL\Types\StringType;
use Doctrine\DBAL\Types\Type;
use Illuminate\Support\Facades\DB;

    public function __construct()
    {
        if (! Type::hasType('enum')) {
            Type::addType('enum', StringType::class);
        }
        // For point types
        // DB::getDoctrineSchemaManager()->getDatabasePlatform()->registerDoctrineTypeMapping('point', 'string');
        DB::getDoctrineSchemaManager()->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string');
    }

Type类是什么? - miken32
我更新了代码:Doctrine\DBAL\Types\Type - William Desportes
1
多年前的这个答案也包括了单独使用registerDoctrineTypeMapping()命令。调用addType()有什么改进呢? - miken32
可能我不会有机会测试它。让我们知道。 - William Desportes

6
你应该避免使用枚举(enum)。即使在 Laravel 5.8 中,问题仍未解决。感谢所有提醒的人。

Laravel 5.1 的官方文档明确表示:

注意:目前不支持重命名具有 enum 列的表中的列。

此外,当向 enum 列声明添加可用选项时,您将遇到相同的问题。

这让我得出一个结论,你应该小心使用枚举(enum),甚至根本不应该使用枚举(enum)

我不能赞同任何要求用字符串替换枚举的答案。不,你需要创建一个查找表,并用 unsignedInteger 作为 foreign key 来替换 enum。

这是很大的工作量,如果没有以前的单元测试覆盖率,你会很沮丧,但这是正确的解决方案。

你可能因为花费太长时间而被解雇,但别担心,你会找到更好的工作的。 :)

以下是向 enum 列声明添加可用选项的一个示例:

假设你有以下内容:

Schema::create('blogs', function (Blueprint $table) {
    $table->enum('type', [BlogType::KEY_PAYMENTS]);
    $table->index(['type', 'created_at']);
...

你需要提供更多可用的类型。

public function up(): void
{
    Schema::table('blogs', function (Blueprint $table) {
        $table->dropIndex(['type', 'created_at']);
        $table->enum('type_tmp', [
            BlogType::KEY_PAYMENTS,
            BlogType::KEY_CATS,
            BlogType::KEY_DOGS,
        ])->after('type');
    });

    DB::statement('update `blogs` as te set te.`type_tmp` = te.`type` ');

    Schema::table('blogs', function (Blueprint $table) {
        $table->dropColumn('type');
    });

    Schema::table('blogs', function (Blueprint $table) {
        $table->enum('type', [
            BlogType::KEY_PAYMENTS,
            BlogType::KEY_CATS,
            BlogType::KEY_DOGS,
        ])->after('type_tmp');
    });

    DB::statement('update `blogs` as te set te.`type` = te.`type_tmp` ');

    Schema::table('blogs', function (Blueprint $table) {
        $table->dropColumn('type_tmp');
        $table->index(['type', 'created_at']);
    });
}

5

我曾经遇到过同样的问题,但是我在 Laravel 10.x 中采用了以下方法解决:

选项 1:使用本地架构操作

你需要在 App\Providers\AppServiceProvider 类的 boot 方法中或者在迁移文件中调用 Schema::useNativeSchemaOperationsIfPossible() 方法以便使用本地架构操作。

参考资料:

选项2:添加Doctrine类型映射

在你的数据库迁移文件中添加这个构造方法。

use Doctrine\DBAL\Types\StringType;
use Doctrine\DBAL\Types\Type;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

....

public function __construct()
{
    if (!Type::hasType('enum')) {
        Type::addType('enum', StringType::class);
    }

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

选项三:使用数据库语句

use Illuminate\Support\Facades\DB;

...

public function up()
{
    DB::statement("ALTER TABLE `table_name` CHANGE `column_name` `column_name` ENUM('option1', 'option2', 'option2') DEFAULT 'option1';");
}

参考资料


4
您可以使用上述建议,也可以将以下代码添加到您的迁移文件中...
public function up()
    {
DB::connection()->getDoctrineSchemaManager()->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string');

Schema::table('<YOUR_TABLE>', function (Blueprint $table) {
//YOUR CHANGES HERE
}    
    }

1
尝试在 Laravel 7.x 上执行此操作,仍然出现错误,但感谢您提供的想法。 - Dika
1
它在我这里在8.x版本上运行正常。虽然我的情况有所不同,我正在检索给定表的模式列表。 - Norgul
这只是从上面的得票最高的答案复制过来的。 - miken32

3

我认为解决这个问题的最简单方法是在doctrine.yaml中添加一个映射类型(如果适用),这样枚举就会被视为字符串。

doctrine:
    dbal:
        #other configuration
        mapping_types:
            enum: string

3
一种真正的“脏”解决方案,虽然不够优美但能完成任务,就是:
update Doctrine/DBAL/Schema/MySqlSchemaManager.php 

在第113行上面添加以下这几行代码:

$this->_platform->registerDoctrineTypeMapping('enum', 'string');
$type = $this->_platform->getDoctrineTypeMapping($dbType);

注意,直接更新供应商文件是不可取的,因为如果供应商选择更新插件,您的更改可能会被覆盖。


我正在做这个,但是它不起作用。protected function _getPortableDatabaseDefinition($database) { $this->_platform->registerDoctrineTypeMapping('enum', 'string'); $type = $this->_platform->getDoctrineTypeMapping($dbType); return $database['Database']; } - andreshg112
无论你在之后添加了多少“注意”,编辑供应商文件始终都不是一个合适的答案。 - miken32

-1
如果您在Doctrine和Laminas设置中遇到以下错误,请查找枚举列的源代码,因为我的源代码中没有类似的内容。
未知的数据库类型enum请求
这是phpMyAdmin在数据库中创建的表模式,Doctrine想要与我的模式定义匹配/同步。它找不到包含在phpMyAdmin模式中的枚举列。
我通过将pma_表移动到另一个数据库来解决这个问题。

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