effiana/migration-bundle

BAP的架构和数据迁移

安装: 314

依赖: 3

建议者: 0

安全性: 0

星标: 0

关注者: 1

分支: 0

开放问题: 0

类型:symfony-bundle

2.0.1 2021-01-07 13:54 UTC

This package is auto-updated.

Last update: 2024-09-07 22:08:42 UTC


README

EffianaMigrationBundle 扩展了 DBAL(数据库抽象层),并提供了使用迁移和固定数据类以一致、结构化的方式管理应用程序数据库架构变化的能力。

数据库结构迁移

每个包都可以有自己的迁移文件,允许更新数据库架构。

迁移文件应位于 Migrations\Schema\version_number 文件夹中。版本号必须是 PHP 标准化的版本号字符串,但有一些限制。这个字符串不能包含 "." 和 "+" 字符作为版本部分分隔符。有关 PHP 标准化版本号字符串的更多信息,请参阅 PHP 手册

每个迁移类必须实现 Migration 接口,并必须实现 up 方法。该方法接收当前数据库结构作为 schema 参数,以及可以用于添加附加查询的 queries 参数。

使用 schema 参数,您可以在不担心数据库引擎兼容性的情况下创建或更新数据库结构。如果您想在应用架构修改前后执行额外的 SQL 查询,可以使用 queries 参数。此参数代表一个 查询包,允许添加将在应用前(使用 addPreQuery 方法)或应用后(使用 addQueryaddPostQuery 方法)执行的附加查询。查询可以是字符串或实现 MigrationQuery 接口的类的实例。该接口有几种现成的实现

如果您需要创建自己的 MigrationQuery 实现,可以使用 ConnectionAwareInterface。只需在需要数据库连接的迁移查询类中实现此接口即可。您还可以使用 ParametrizedMigrationQuery 类作为迁移查询的基础类。

如果您在同一版本中有多个迁移类,并且需要确保它们按指定顺序执行,可以使用 OrderedMigrationInterface 接口。

迁移文件示例

<?php

namespace Acme\Bundle\TestBundle\Migrations\Schema\v1_0;

use Doctrine\DBAL\Schema\Schema;
use Effiana\MigrationBundle\Migration\Migration;
use Effiana\MigrationBundle\Migration\QueryBag;
use Effiana\MigrationBundle\Migration\Extension\RenameExtension;
use Effiana\MigrationBundle\Migration\Extension\RenameExtensionAwareInterface;

class AcmeTestBundle implements Migration, RenameExtensionAwareInterface
{
    /**
     * @var RenameExtension
     */
    protected $renameExtension;

    /**
     * @inheritdoc
     */
    public function setRenameExtension(RenameExtension $renameExtension)
    {
        $this->renameExtension = $renameExtension;
    }

    /**
     * @inheritdoc
     */
    public function up(Schema $schema, QueryBag $queries)
    {
        $table = $schema->createTable('test_table');
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
        $table->addColumn('created', 'datetime', []);
        $table->addColumn('field', 'string', ['length' => 500]);
        $table->addColumn('another_field', 'string', ['length' => 255]);
        $table->setPrimaryKey(['id']);

        $this->renameExtension->renameTable(
            $schema,
            $queries,
            'old_table_name',
            'new_table_name'
        );
        $queries->addQuery(
            "ALTER TABLE another_table ADD COLUMN test_column INT NOT NULL",
        );
    }
}

每个包都可以有一个安装文件。这个迁移文件替换了运行多个迁移文件。安装迁移类必须实现 安装 接口,并必须实现 upgetMigrationVersion 方法。 getMigrationVersion 方法必须返回此安装文件替换的最大迁移版本号。

在安装过程中(这意味着您从头开始安装系统),如果找到安装迁移文件,它将首先加载,然后加载由 getMigrationVersion 方法返回的版本号更大的迁移文件。

例如。我们有 v1_0v1_1v1_2v1_3 迁移。此外,我们还有一个安装迁移类。此类返回 v1_2 作为迁移版本。因此,在安装过程中,将加载安装迁移文件,然后仅加载 v1_3 迁移文件。从 v1_0v1_2 的迁移将不会加载。

安装迁移文件的示例

<?php

namespace Acme\Bundle\TestBundle\Migrations\Schema;

use Doctrine\DBAL\Schema\Schema;
use Effiana\MigrationBundle\Migration\Installation;
use Effiana\MigrationBundle\Migration\QueryBag;

class AcmeTestBundleInstaller implements Installation
{
    /**
     * @inheritdoc
     */
    public function getMigrationVersion()
    {
        return 'v1_1';
    }

    /**
     * @inheritdoc
     */
    public function up(Schema $schema, QueryBag $queries)
    {
        $table = $schema->createTable('test_installation_table');
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
        $table->addColumn('field', 'string', ['length' => 500]);
        $table->setPrimaryKey(['id']);
    }
}

要运行迁移,有 effiana:migration:load 命令。此命令从包中收集迁移文件,按版本号排序并应用更改。

此命令支持一些附加选项

  • force - 导致由迁移生成的 SQL 语句物理执行到您的数据库;
  • dry-run - 输出迁移列表而不应用它们;
  • show-queries - 输出每个迁移文件的数据库查询列表;
  • bundles - 要加载数据的包列表。如果未设置选项,则从所有包中获取迁移;
  • exclude - 要跳过的迁移包名称列表。

此外,还有 effiana:migration:dump 命令,有助于创建安装文件。此命令将当前数据库结构输出为纯 SQL 或 Doctrine\DBAL\Schema\Schema 查询。

此命令支持一些附加选项

  • plain-sql - 以纯 SQL 查询输出模式
  • bundle - 将生成迁移的包名称
  • migration-version - 迁移版本号。此选项将设置由生成的安装文件的 getMigrationVersion 方法返回的值。

对于包来说,有一个当前版本的安装文件和从先前版本迁移到当前版本的迁移文件是一个好的做法。

以下算法可用于您包的新版本

  • 创建新的迁移
  • 使用 effiana:migration:load 应用它
  • 使用 effiana:migration:dump 生成新的安装文件
  • 如果需要 - 向生成的安装文件中添加迁移扩展调用。

数据库结构迁移的扩展

有时您不能使用标准的 Doctrine 方法来修改数据库结构。例如,Schema::renameTable 不工作,因为它删除了现有的表然后创建了一个新的表。为了帮助您处理此类情况并向任何迁移添加一些有用的功能,设计了一种扩展机制。以下示例显示了如何使用 RenameExtension

<?php

namespace Acme\Bundle\TestBundle\Migrations\Schema\v1_0;

use Doctrine\DBAL\Schema\Schema;
use Effiana\MigrationBundle\Migration\Migration;
use Effiana\MigrationBundle\Migration\QueryBag;
use Effiana\MigrationBundle\Migration\Extension\RenameExtension;
use Effiana\MigrationBundle\Migration\Extension\RenameExtensionAwareInterface;

class AcmeTestBundle implements Migration, RenameExtensionAwareInterface
{
    /**
     * @var RenameExtension
     */
    protected $renameExtension;

    /**
     * @inheritdoc
     */
    public function setRenameExtension(RenameExtension $renameExtension)
    {
        $this->renameExtension = $renameExtension;
    }

    /**
     * @inheritdoc
     */
    public function up(Schema $schema, QueryBag $queries)
    {
        $this->renameExtension->renameTable(
            $schema,
            $queries,
            'old_table_name',
            'new_table_name'
        );
    }
}

如您所见,要使用 RenameExtension,您的迁移类应实现 RenameExtensionAwareInterfacesetRenameExtension 方法。此外,还有一些您可以在迁移类中使用的一些其他有用的接口

为数据库结构迁移创建自己的扩展

要创建自己的扩展,您需要执行以下简单的步骤

  • YourBundle/Migration/Extension 目录中创建一个扩展类。使用 YourBundle/Migration/Extension 目录不是强制的,但强烈推荐。例如
<?php

namespace Acme\Bundle\TestBundle\Migration\Extension;

use Doctrine\DBAL\Schema\Schema;
use Effiana\MigrationBundle\Migration\QueryBag;

class MyExtension
{
    public function doSomething(Schema $schema, QueryBag $queries, /* other parameters, for example */ $tableName)
    {
        $table = $schema->getTable($tableName); // highly recommended to make sure that a table exists
        $query = 'SOME SQL'; /* or $query = new SqlMigrationQuery('SOME SQL'); */

        $queries->addQuery($query);
    }
}
  • 在同一个命名空间中创建 *AwareInterface。重要的是接口名称应该是 {ExtensionClass}AwareInterface,并且设置方法应该是 set{ExtensionClass}({ExtensionClass} ${extensionName})。例如
<?php

namespace Acme\Bundle\TestBundle\Migration\Extension;

/**
 * MyExtensionAwareInterface should be implemented by migrations that depends on a MyExtension.
 */
interface MyExtensionAwareInterface
{
    /**
     * Sets the MyExtension
     *
     * @param MyExtension $myExtension
     */
    public function setMyExtension(MyExtension $myExtension);
}
  • 在依赖注入容器中注册扩展。例如
parameters:
    acme_test.migration.extension.my.class: Acme\Bundle\TestBundle\Migration\Extension\MyExtension

services:
    acme_test.migration.extension.my:
        class: %acme_test.migration.extension.my.class%
        tags:
            - { name: effiana_migration.extension, extension_name: test /*, priority: -10 - priority attribute is optional an can be helpful if you need to override existing extension */ }

如果您需要访问数据库平台或名称生成器,您应该相应地实现 DatabasePlatformAwareInterfaceNameGeneratorAwareInterface。此外,如果您需要在您的扩展中使用其他扩展,扩展类只需实现所需的扩展的 *AwareInterface 即可。

数据固定

Symfony 允许使用数据固定加载数据。但是,这些固定每次执行 doctrine:fixtures:load 命令时都会运行。

为了避免多次加载相同的固定,创建了 effiana:migration:data:load 命令。此命令保证每个数据固定只加载一次。

此命令支持两种类型的迁移文件:main 数据固定和 demo 数据固定。在安装过程中,用户可以选择是否加载演示数据。

此命令的数据固定应放在 Migrations/Data/ORMMigrations/Data/Demo/ORM 目录中。

固定顺序可以使用标准的 Doctrine 排序或依赖功能进行更改。有关固定排序的更多信息,请参阅 doctrine 数据固定手册

版本化固定

有一些固定需要反复执行。例如,上传国家数据的固定。通常,如果您添加了新的国家列表,您需要创建新的数据固定来上传这些数据。为了避免这种情况,您可以使用版本化数据固定。

要使固定版本化,该固定必须实现 VersionedFixtureInterface 和返回固定数据版本的 getVersion 方法。

示例

<?php

namespace Acme\DemoBundle\Migrations\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\Persistence\ObjectManager;

use Effiana\MigrationBundle\Fixture\VersionedFixtureInterface;

class LoadSomeDataFixture extends AbstractFixture implements VersionedFixtureInterface
{
    /**
     * {@inheritdoc}
     */
    public function getVersion()
    {
        return '1.0';
    }

    /**
     * {@inheritdoc}
     */
    public function load(ObjectManager $manager)
    {
        // Here we can use fixture data code which will be run time after time
    }
}

在此示例中,如果固定尚未加载,则将加载它,并将版本 1.0 保存为该固定当前加载的版本。

为了有重新加载此固定的可能性,固定必须返回一个大于 1.0 的版本,例如 1.0.1 或 1.1。版本号必须是一个 PHP 标准化的版本号字符串。有关 PHP 标准化版本号字符串的更多信息,请参阅 PHP 手册

如果固定需要知道最后加载的版本,则必须实现 LoadedFixtureVersionAwareInterfacesetLoadedVersion 方法

<?php

namespace Acme\DemoBundle\Migrations\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\Persistence\ObjectManager;

use Effiana\MigrationBundle\Fixture\VersionedFixtureInterface;
use Effiana\MigrationBundle\Fixture\RequestVersionFixtureInterface;

class LoadSomeDataFixture extends AbstractFixture implements VersionedFixtureInterface, LoadedFixtureVersionAwareInterface
{
    /**
     * @var $currendDBVersion string
     */
    protected $currendDBVersion = null;

    /**
     * {@inheritdoc}
     */
    public function setLoadedVersion($version = null)
    {
        $this->currendDBVersion = $version;
    }

    /**
     * {@inheritdoc}
     */
    public function getVersion()
    {
        return '2.0';
    }

    /**
     * {@inheritdoc}
     */
    public function load(ObjectManager $manager)
    {
        // Here we can check last loaded version and load data data difference between last
        // uploaded version and current version
    }
}