Laravel迁移事务

时间:2013-12-06 09:40:15

标签: php laravel migrate

开发时,我在laravel中遇到了很多迁移问题。

我创建了一个迁移。当我完成创建时,迁移中间(例如,外键约束)会出现一个小错误,使“php artisan migrate”失败。他确实告诉我错误的位置,然后迁移到一个不一致的状态,在错误发生之前对数据库进行了所有修改,而不是下一个修改。

这使得当我修复错误并重新运行migrate时,第一个语句失败,因为已经创建/修改了列/表。然后我知道的唯一解决方案是进入我的数据库并手动“回滚”所有内容,这样做的时间更长。

migrate:rollback尝试回滚以前的迁移,因为当前未成功应用当前迁移。

我还尝试将所有代码都包装到DB :: transaction()中,但它仍然不起作用。

这有什么解决方案吗?或者我只需要手动回滚东西?



编辑,添加一个例子(不编写Schema builder代码,只是某种伪代码):
Migration1:

Create Table users (id, name, last_name, email)

Migration1执行正常。几天后,我们进行了迁移2:

Create Table items (id, user_id references users.id)
Alter Table users make_some_error_here

现在将会发生的事情是,migrate将调用第一个语句,并使用外键为用户创建表项。然后,当他试图应用下一个语句时,它将失败。

如果我们修复了make_some_error_here,我们就无法运行migrate,因为它创建了表“items”。我们无法回滚(也不刷新,也不能重置),因为我们无法删除表用户,因为表项中存在外键约束。

然后,继续的唯一方法是转到数据库并手动删除表项,以便以一致的状态进行迁移。

5 个答案:

答案 0 :(得分:12)

这不是Laravel限制,我打赌你使用MYSQL,对吗?

正如MYSQL文档所说here

  

某些语句无法回滚。通常,这些包括数据   定义语言(DDL)语句,例如那些创建或   drop database,创建,删除或更改表或存储的数据库   例程。

我们建议泰勒奥特威尔自己here说:

  

我最好的建议是每次迁移执行一次操作,以便您的   迁移过程非常精细。

答案 1 :(得分:4)

我使用的是MySql,我遇到了这个问题。

我的解决方案取决于您的down()方法与up()中的方法完全相同,但后退。

这就是我要去的地方:

try{
    Schema::create('table1', function (Blueprint $table) {
        //...
    });
    Schema::create('tabla2', function (Blueprint $table) {
        //...
    });
}catch(PDOException $ex){
    $this->down();
    throw $ex;
}

因此,如果某些内容失败,则自动调用down()方法并再次抛出异常。

而不是在transaction()之间使用迁移,而不是在此尝试之间执行

答案 2 :(得分:1)

只需从迁移文件中删除失败的代码,然后为失败的语句生成新的迁移。现在,当它再次失败时,数据库的创建仍然完好无损,因为它存在于另一个迁移文件中。

使用这种方法的另一个好处是,在还原数据库时,您可以获得更多控制和更小的步骤。

希望有所帮助:D

答案 3 :(得分:1)

我知道这是一个老话题,但是一个月前有活动,所以这是我的2美分。

此答案适用于MySql 8和Laravel 5.8 从MySql 8开始,MySql引入了原子DDL:https://dev.mysql.com/doc/refman/8.0/en/atomic-ddl.html Laravel在迁移开始时会检查模式语法是否支持事务中的迁移,以及是否确实启动了它。 问题在于MySql模式语法已将其设置为false。我们可以扩展Migrator,MySql模式语法和MigrationServiceProvider,然后像这样注册服务提供者:

<?php

namespace App\Console;

use Illuminate\Database\Migrations\Migrator as BaseMigrator;

use App\Database\Schema\Grammars\MySqlGrammar;

class Migrator extends BaseMigrator {
    protected function getSchemaGrammar( $connection ) {
        if ( get_class( $connection ) === 'Illuminate\Database\MySqlConnection' ) {
            $connection->setSchemaGrammar( new MySqlGrammar );
        }
        if ( is_null( $grammar = $connection->getSchemaGrammar() ) ) {
            $connection->useDefaultSchemaGrammar();
            $grammar = $connection->getSchemaGrammar();
        }
        return $grammar;
    }
}


<?php

namespace App\Database\Schema\Grammars;

use Illuminate\Database\Schema\Grammars\MySqlGrammar as BaseMySqlGrammar;

class MySqlGrammar extends BaseMySqlGrammar {
    public function __construct() {
        $this->transactions = config( "database.transactions", false );
    }
}


<?php

namespace App\Providers;

use Illuminate\Database\MigrationServiceProvider as BaseMigrationServiceProvider;

use App\Console\Migrator;

class MigrationServiceProvider extends BaseMigrationServiceProvider {   
    /**
     * Register the migrator service.
     * @return void
     */
    protected function registerMigrator() {
        $this->app->singleton( 'migrator', function( $app ) {
            return new Migrator( $app[ 'migration.repository' ], $app[ 'db' ], $app[ 'files' ] );
        } );
        $this->app->singleton(\Illuminate\Database\Migrations\Migrator::class, function ( $app ) {
            return $app[ 'migrator' ];
        } );
    }


<?php

return [
    'providers' => [

        /*
         * Laravel Framework Service Providers...
         */
        App\Providers\MigrationServiceProvider::class,

    ],
];

当然,我们必须将事务添加到我们的数据库配置中... 免责声明-尚未测试,但仅查看代码即可按广告宣传的方式工作:)更新以进行测试...

答案 4 :(得分:0)

我认为最好的方法就像文档中所示:

DB::transaction(function () {
    DB::table('users')->update(['votes' => 1]);

    DB::table('posts')->delete();
});

请参阅:https://laravel.com/docs/5.8/database#database-transactions