学说迁移后备

发布于 2024-10-12 12:34:32 字数 337 浏览 9 评论 0原文

我们正在使用原则迁移,当迁移包含多个操作并且其中一个操作失败时,通常会出现问题。

例如,如果迁移添加了 5 个外键,其中第 5 个失败,而字段长度不同,则修复字段错误并重新生成迁移并不能解决整个问题,而现在出现一个错误,该错误与 4 个密钥已经存在并且不允许迁移成功运行有关。

有没有一种稳定的方法来使用 Doctrine 迁移而不会出现上述的明显问题?我们以前使用过 .sql 文件,实际上并没有好多少,但我很确定对于使用 Doctrine 的数据库版本控制有正确的方法项目?

根据模型和模式之间的差异生成迁移非常棒,我想进一步保留这种可能性。

谢谢

We're using doctrine migrations and there often are problems when the migration contains multiple actions and one of them fails.

For example, if there is a migration adding 5 foreign keys and the 5th of them fails while fields aren't of the same length, fixing the error with the fields and regenerating migrations does not fix the whole thing, while now there is an error connected with the fact 4 of the keys already exists and don't allow the migration to run successfully.

Is there a stable way to use Doctrine migrations without such obvious problems as mentioned? We've used .sql files previosly, which aren't much better actually, but I'm pretty sure there is the right way of database versioning for a Doctrine-using project?

Generating migrations based on the difference between models and schema is great and I'd like to keep this possibility furthermore.

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

我乃一代侩神 2024-10-19 12:34:32

我解决了这个问题,该解决方案并不是那么好,但我想它对其他人仍然有用。我确实正在使用 CLI,我已经完成了文件,使每次迁移都会更新数据库中的数字,类似于提莫在提出这个问题之前的回答中的文件,但这仍然不是很有效,但无论如何值得这样做。

我所做的下一步解决了问题,请转到
doctrine/lib/Doctrine/Migration/Builder.php 第 531 行。这里定义了每次迁移都会扩展的默认类。由于我使用的是 CLI,并且无法找到将参数传递到此位置的方法,因此我刚刚将 Doctrine_Migration_Base 替换为下面的另一个类 MY_Doctrine_Migration_Base

如果您不使用 CLI,我想说您应该尝试传递选项而不是替换源。

因此,下面的类扩展了 Doctrine_Migration_Base 并覆盖了一堆方法,检查是否可以进行更改,然后调用父方法来执行这些更改。它没有涵盖当前的所有方法,仅涵盖我在编写本文时遇到的方法。

现在,每一个迁移原则创建的扩展都扩展了我的类,其目的是防止我最初提到的问题。

<?php

class MY_Doctrine_Migration_Base extends Doctrine_Migration_Base {
    public function __construct() {
        $this->connection = Doctrine_Manager::getInstance()->getCurrentConnection();
    }

    public function addIndex($tableName, $indexName, array $definition) {
        foreach ($this->connection->execute("SHOW INDEXES IN $tableName")->fetchAll(PDO::FETCH_ASSOC) as $index) {
            if ($index['Key_name'] === $indexName.'_idx') {
                echo "Index $indexName already exists in table $tableName. Skipping\n";
                return;
            }
        }

        parent::addIndex($tableName, $indexName, $definition);
    }

    public function removeColumn($tableName, $columnName) {
        if ($this->column_exists($tableName, $columnName)) {
            parent::removeColumn($tableName, $columnName);
        } else {
            echo "Column $columnName doesn't exist in $tableName. Can't drop\n";
        }
    }

    public function createTable($tableName, array $fields = array(), array $options = array()) {
        if ($this->connection->execute("SHOW TABLES LIKE '$tableName'")->fetchAll(PDO::FETCH_ASSOC)) {
            echo "Table $tableName already exists. Can't create\n";
        } else {
            parent::createTable($tableName, $fields, $options);
        }
    }

    public function addColumn($tableName, $columnName, $type, $length = null, array $options = array()) {
        if (! $this->column_exists($tableName, $columnName)) {
            parent::addColumn($tableName, $columnName, $type, $length, $options);
        } else {
            echo "Column $columnName already exists in $tableName. Can't add\n";
        }
    }

    private function column_exists($tableName, $columnName) {
        $exception = FALSE;

        try { //parsing information_schema sucks because security will hurt too bad if we have access to it. This lame shit is still better
            $this->connection->execute("SELECT $columnName FROM $tableName")->fetchAll(PDO::FETCH_ASSOC);
        } catch (Exception $exception) {}
        //if someone knows how to check for column existence without exceptions AND WITHOUT INFORMATION SCHEMA please rewrite this stuff

        return $exception === FALSE;
    }
}

欢迎提出有关如何改进这一点的建议。

I kind of solved this, the solution isn't all that nice, but still, I guess it will be useful to other people. I'm using CLI indeed I've already done the file making every migration update the number in the database, similar to the one in the Timo's answer before asking this question, but that still isn't very effective but worth doing anyway.

What I've done next kind of solves stuff, go to
doctrine/lib/Doctrine/Migration/Builder.php line 531. There is the definition of the default class every migration will extends. Since I'm using CLI and could not find a way to pass parameters to this place I've just replaced Doctrine_Migration_Base to another class MY_Doctrine_Migration_Base which is below.

If you're not using CLI I'd say you should try to pass options and not replace source.

So the below class extends Doctrine_Migration_Base and overwrites a bunch of methods to the ones, checking whether it's OK to make changes and then calling parent method to do them. It doesn't cover all the methods currently, just the ones I've encountered when I wrote this.

Now every migration Doctrine creates extends my class which is aimed at preventing the problems I mentioned originally.

<?php

class MY_Doctrine_Migration_Base extends Doctrine_Migration_Base {
    public function __construct() {
        $this->connection = Doctrine_Manager::getInstance()->getCurrentConnection();
    }

    public function addIndex($tableName, $indexName, array $definition) {
        foreach ($this->connection->execute("SHOW INDEXES IN $tableName")->fetchAll(PDO::FETCH_ASSOC) as $index) {
            if ($index['Key_name'] === $indexName.'_idx') {
                echo "Index $indexName already exists in table $tableName. Skipping\n";
                return;
            }
        }

        parent::addIndex($tableName, $indexName, $definition);
    }

    public function removeColumn($tableName, $columnName) {
        if ($this->column_exists($tableName, $columnName)) {
            parent::removeColumn($tableName, $columnName);
        } else {
            echo "Column $columnName doesn't exist in $tableName. Can't drop\n";
        }
    }

    public function createTable($tableName, array $fields = array(), array $options = array()) {
        if ($this->connection->execute("SHOW TABLES LIKE '$tableName'")->fetchAll(PDO::FETCH_ASSOC)) {
            echo "Table $tableName already exists. Can't create\n";
        } else {
            parent::createTable($tableName, $fields, $options);
        }
    }

    public function addColumn($tableName, $columnName, $type, $length = null, array $options = array()) {
        if (! $this->column_exists($tableName, $columnName)) {
            parent::addColumn($tableName, $columnName, $type, $length, $options);
        } else {
            echo "Column $columnName already exists in $tableName. Can't add\n";
        }
    }

    private function column_exists($tableName, $columnName) {
        $exception = FALSE;

        try { //parsing information_schema sucks because security will hurt too bad if we have access to it. This lame shit is still better
            $this->connection->execute("SELECT $columnName FROM $tableName")->fetchAll(PDO::FETCH_ASSOC);
        } catch (Exception $exception) {}
        //if someone knows how to check for column existence without exceptions AND WITHOUT INFORMATION SCHEMA please rewrite this stuff

        return $exception === FALSE;
    }
}

Suggestions on how to improve this are welcome.

风向决定发型 2024-10-19 12:34:32

教义迁移无法解决这个问题。很抱歉,我们都有这些问题,因为迁移不是在事务中运行的。

您可以通过添加插件来改进这一点。请参阅:博客文章

另一种可能性是在迁移之前进行数据库备份,如果出现问题,您可以重新安装备份。您可以通过 shell 脚本自动执行此操作

Doctrine migrations can not handle this. Sorry to say that we all have these problems, because the migrations didn't run in a transaction.

You can improve this by adding a plugin. See: Blog-Post

The other possibility is to do a database backup before migrating and if something goes wrong you can reinstall the backup. You can automate this by a shell script

暗喜 2024-10-19 12:34:32

如果您使用的是doctrine-cli,您可以编写自己的迁移任务,在迁移之前备份数据库,并在迁移失败时恢复备份。我为我们的 symfony/doctrine 迁移写了类似的东西。

如果您将任务类放在正确的目录中,学说 cli 会将其显示在可用命令列表中

If you're using the doctrine-cli you can write your own migration task that backs up the database before the migration and restores the back up if the migration fails. I wrote something similar for our symfony/doctrine migrations.

If you put your task class in the correct directory the doctrine cli will display it in the list of available commands

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文