跳到内容

DoctrineMigrationsBundle

编辑此页

数据库迁移是一种安全更新本地和生产环境数据库模式的方法。与运行 doctrine:schema:update 命令或使用 SQL 语句手动应用数据库更改不同,迁移允许以安全的方式复制数据库模式中的更改。

Symfony 应用程序中通过 DoctrineMigrationsBundle 提供迁移,它使用外部 Doctrine Database Migrations 库。如果您需要关于迁移的通用介绍,请阅读该库的文档

安装

在终端中运行此命令

1
$ composer require doctrine/doctrine-migrations-bundle "^3.0"

如果您不使用 Symfony Flex,则必须在应用程序中手动启用该 bundle

1
2
3
4
5
6
// config/bundles.php

return [
    // ...
    Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
];

配置

如果您使用 Symfony Flex,则会自动创建 doctrine_migrations.yaml 配置文件。否则,创建以下文件并为您的应用程序配置它

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# config/packages/doctrine_migrations.yaml

doctrine_migrations:
    # List of namespace/path pairs to search for migrations, at least one required
    migrations_paths:
        'App\Migrations': '%kernel.project_dir%/src/App'
        'AnotherApp\Migrations': '/path/to/other/migrations'
        'SomeBundle\Migrations': '@SomeBundle/Migrations'

    # List of additional migration classes to be loaded, optional
    migrations:
        - 'App\Migrations\Version123'
        - 'App\Migrations\Version123'

    # Connection to use for the migrations
    connection: default

    # Entity manager to use for migrations. This overrides the "connection" setting.
    em: default

    storage:
        # Default (SQL table) metadata storage configuration
        table_storage:
            table_name: 'doctrine_migration_versions'
            version_column_name: 'version'
            version_column_length: 192
            executed_at_column_name: 'executed_at'

    # Possible values: "BY_YEAR", "BY_YEAR_AND_MONTH", false
    organize_migrations: false

    # Path to your custom migrations template
    custom_template: ~

    # Run all migrations in a transaction.
    all_or_nothing: false

    # Adds an extra check in the generated migrations to ensure that is executed on the same database type.
    check_database_platform: true

    # Whether or not to wrap migrations in a single transaction.
    transactional: true

    # Whether or not to enable the profiler collector to calculate and visualize migration status. This adds some queries overhead.
    # enable_profiler: false

    services:
        # Custom migration sorting service id
        'Doctrine\Migrations\Version\Comparator': ~

        # Custom migration classes factory
        'Doctrine\Migrations\Version\MigrationFactory': ~

    factories:
        # Custom migration sorting service id via callables (MyCallableFactory must be a callable)
        'Doctrine\Migrations\Version\Comparator': 'MyCallableFactory'
  • services 节点允许您为 doctrine/migrations 的底层 DependencyFactory 部分提供自定义服务。
  • factories 节点类似于 services,不同之处在于它只接受可调用对象。

提供的可调用对象必须返回要传递给 DependencyFactory 的服务。可调用对象将接收 DependencyFactory 自身作为第一个参数,允许您在实例化自定义依赖项时从工厂获取其他依赖项。

用法

所有迁移功能都包含在几个控制台命令中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
doctrine
 doctrine:migrations:current                [current] Outputs the current version.
 doctrine:migrations:diff                   [diff] Generate a migration by comparing your current database to your mapping information.
 doctrine:migrations:dump-schema            [dump-schema] Dump the schema for your database to a migration.
 doctrine:migrations:execute                [execute] Execute a single migration version up or down manually.
 doctrine:migrations:generate               [generate] Generate a blank migration class.
 doctrine:migrations:latest                 [latest] Outputs the latest version number
 doctrine:migrations:migrate                [migrate] Execute a migration to a specified version or the latest available version.
 doctrine:migrations:rollup                 [rollup] Roll migrations up by deleting all tracked versions and inserting the one version that exists.
 doctrine:migrations:status                 [status] View the status of a set of migrations.
 doctrine:migrations:up-to-date             [up-to-date] Tells you if your schema is up-to-date.
 doctrine:migrations:version                [version] Manually add and delete migration versions from the version table.
 doctrine:migrations:sync-metadata-storage  [sync-metadata-storage] Ensures that the metadata storage is at the latest version.
 doctrine:migrations:list                   [list-migrations] Display a list of all available migrations and their status.

首先,通过运行 status 命令获取应用程序中迁移的状态

1
$ php bin/console doctrine:migrations:status

此命令将显示关于迁移状态的通用信息,例如已执行的迁移数量、仍需要运行的迁移以及正在使用的数据库。

现在,您可以通过生成新的空白迁移类来开始使用迁移。稍后,您将学习 Doctrine 如何为您自动生成迁移。

1
$ php bin/console doctrine:migrations:generate

查看新生成的迁移类,您将看到如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

/**
 * Auto-generated Migration: Please modify to your needs!
 */
final class Version20180605025653 extends AbstractMigration
{
    public function getDescription() : string
    {
        return '';
    }

    public function up(Schema $schema) : void
    {
        // this up() migration is auto-generated, please modify it to your needs

    }

    public function down(Schema $schema) : void
    {
        // this down() migration is auto-generated, please modify it to your needs

    }
}

如果您再次运行 status 命令,它现在将显示您有一个新的迁移要执行

1
$ php bin/console doctrine:migrations:status

现在您可以向 up()down() 方法添加一些迁移代码,并在准备就绪时最终迁移

1
$ php bin/console doctrine:migrations:migrate 'DoctrineMigrations\Version20180605025653'

有关如何编写迁移本身(即,如何填写 up()down() 方法)的更多信息,请参阅官方 Doctrine Migrations 文档

在部署期间运行迁移

当然,编写迁移的最终目标是能够在部署应用程序时使用它们可靠地更新数据库结构。通过在本地(或在 beta 服务器上)运行迁移,您可以确保迁移按预期工作。

当您最终部署应用程序时,您只需要记住运行 doctrine:migrations:migrate 命令。在内部,Doctrine 会在您的数据库中创建一个 migration_versions 表,并跟踪哪些迁移已在那里执行。因此,无论您在本地创建和执行了多少迁移,当您在部署期间运行该命令时,Doctrine 都会通过查看生产数据库的 migration_versions 表来准确知道哪些迁移尚未运行。无论您在哪个服务器上,您始终可以安全地运行此命令,以仅执行尚未在特定数据库上运行的迁移。

跳过迁移

您可以通过显式将单个迁移添加到 migration_versions 表来跳过它们

1
$ php bin/console doctrine:migrations:version 'App\Migrations\Version123' --add

提示

请注意上面命令中使用的单引号 ('),没有它们或使用双引号 (") 该命令将无法正常工作。

然后 Doctrine 将假定此迁移已运行并将忽略它。

迁移依赖

迁移可以依赖于外部服务(例如地理位置、邮件程序、数据处理服务...),这些服务可用于进行更强大的迁移。这些依赖项不会自动注入到您的迁移中,但需要使用自定义迁移工厂注入。

以下是如何将服务容器注入到您的迁移中的示例

1
2
3
4
5
6
7
8
9
10
11
12
# config/packages/doctrine_migrations.yaml

doctrine_migrations:
    services:
         'Doctrine\Migrations\Version\MigrationFactory': 'App\Migrations\Factory\MigrationFactoryDecorator'

# config/services.yaml

services:
    App\Migrations\Factory\MigrationFactoryDecorator:
        decorates: 'doctrine.migrations.migrations_factory'
        arguments: ['@.inner', '@service_container']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
declare(strict_types=1);

namespace App\Migrations\Factory;

use Doctrine\Migrations\AbstractMigration;
use Doctrine\Migrations\Version\MigrationFactory;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class MigrationFactoryDecorator implements MigrationFactory
{
    private $migrationFactory;
    private $container;

    public function __construct(MigrationFactory $migrationFactory, ContainerInterface $container)
    {
        $this->migrationFactory = $migrationFactory;
        $this->container        = $container;
    }

    public function createVersion(string $migrationClassName): AbstractMigration
    {
        $instance = $this->migrationFactory->createVersion($migrationClassName);

        if ($instance instanceof ContainerAwareInterface) {
            $instance->setContainer($this->container);
        }

        return $instance;
    }
}

提示

如果您的迁移类实现了接口 Symfony\Component\DependencyInjection\ContainerAwareInterface,则此 bundle 将自动将默认的 symfony 容器注入到您的迁移类中(这是因为此示例中显示的 MigrationFactoryDecorator 是此 bundle 使用的默认迁移工厂)。

注意

接口 Symfony\Component\DependencyInjection\ContainerAwareInterface 已在 Symfony 6.4 中弃用,并在 7.0 中删除。如果您使用此版本或更高版本,则目前无法将服务容器注入到迁移中。

自动生成迁移

实际上,您应该很少需要手动编写迁移,因为迁移库可以通过比较您的 Doctrine 映射信息(即数据库应该是什么样子)与您当前的实际数据库结构来自动生成迁移类。

例如,假设您创建一个新的 User 实体并为 Doctrine 的 ORM 添加映射信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// src/Entity/User.php
namespace App\Entity;

use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;

#[Entity]
#[Table(name: 'user')]
class User
{
    #[ORM\Id]
    #[ORM\GeneratedValue(strategy: 'AUTO')]
    #[ORM\Column(type: Types:INT)]
    private $id;

    #[ORM\Column(type: Types:STRING, length: 255)]
    private $name;

有了这些信息,Doctrine 现在可以帮助您将新的 User 对象持久化到 user 表并从中检索。当然,此表尚不存在!通过运行以下命令自动为此表生成新的迁移

1
$ php bin/console doctrine:migrations:diff

您应该看到一条消息,指出已基于模式差异生成了一个新的迁移类。如果您打开此文件,您会发现它具有创建 user 表所需的 SQL 代码。接下来,运行迁移以将表添加到您的数据库

1
$ php bin/console doctrine:migrations:migrate

这个故事的寓意是:在您每次更改 Doctrine 映射信息后,运行 doctrine:migrations:diff 命令以自动生成您的迁移类。

如果您从项目的最开始就执行此操作(即,以便即使是第一个表也通过迁移类加载),您将始终能够创建一个全新的数据库并运行您的迁移,以便使您的数据库模式完全是最新的。实际上,这是您项目的一个简单而可靠的工作流程。

如果您不想使用此工作流程,而是通过 doctrine:schema:create 创建模式,您可以告诉 Doctrine 跳过所有现有迁移

1
$ php bin/console doctrine:migrations:version --add --all

否则 Doctrine 将尝试运行所有迁移,这可能无法正常工作。

Doctrine 未管理的手动表

除了基于您的 doctrine 实体生成的数据库结构外,您可能还需要一些自定义表。默认情况下,此类表将被 doctrine:migrations:diff 命令标记为删除。

以下是如何配置 doctrine/dbal 以忽略某些表

1
2
3
4
5
# config/packages/doctrine.yaml

doctrine:
    dbal:
        schema_filter: ~^(?!t_)~ # Ignore all tables prefixed by `t_`

这会忽略 DBAL 级别的表和任何命名对象(例如序列),并且 diff 命令将忽略它们。

请注意,如果您配置了多个连接,则 schema_filter 配置需要按连接放置。

解决元数据存储不同步问题

doctrine/migrations 依赖于连接字符串中正确配置的数据库服务器版本来管理存储迁移的表,也称为元数据存储。

如果您在运行命令 doctrine:migrations:migrate 或建议的命令本身 doctrine:migrations:sync-metadata-storage 时遇到错误 元数据存储不是最新的,请运行 sync-metadata-storage 命令来解决此问题。,请检查数据库连接字符串,并确保定义了正确的服务器版本。如果您正在运行 MariaDB 数据库,则应在服务器版本前加上 mariadb- 前缀(例如:mariadb-10.2.12)。请参阅 configuring_database 部分。

MariaDB 的连接字符串示例

1
DATABASE_URL=mysql://root:@127.0.0.1:3306/testtest?serverVersion=mariadb-10.4.11
这项工作,包括代码示例,根据 Creative Commons BY-SA 3.0 许可获得许可。
目录
    版本