ramunasd/migration-bundle

此包已被废弃且不再维护。未建议替代包。

Doctrine的架构和数据迁移

安装: 159

依赖: 0

建议者: 0

安全: 0

星标: 1

关注者: 2

分支: 0

开放问题: 2

类型:symfony-bundle

1.2 2016-03-01 10:19 UTC

This package is auto-updated.

Last update: 2023-12-29 02:32:20 UTC


README

Build Status Scrutinizer

基于Doctrine的数据库架构和固定数据操作器。

概述

MigrationBundle是开源ORO平台迁移包的衍生版本。ORO开发者创建了一个非常好的工具,但他们不感兴趣于社区的贡献。这就是为什么我们衍生了这个包,使其对每个人都可以使用。

特性

  • 数据库无关迁移
  • 语义化迁移版本
  • 快速安装到最新版本
  • 自动安装器生成
  • 版本化固定数据和示例数据
  • 自定义扩展
  • 迁移钩子

安装

将包添加到composer.json

composer require ramunasd/migration-bundle

然后将包添加到应用程序内核

// app/AppKernel.php
<?php

use Symfony\Component\HttpKernel\Kernel;

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = array(
            // ...
            new Rdv\Bundle\MigrationBundle\RdvMigrationBundle(),
        );
    }

    // ...
}

配置

rdv_migration:
    migration_path: "Migrations/Schema"
    fixtures_path_main: "Migrations/Data/ORM"
    fixtures_path_demo: "Migrations/Data/Demo/ORM"

数据库结构迁移

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

迁移文件应位于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 RDV\Bundle\MigrationBundle\Migration\Migration;
use RDV\Bundle\MigrationBundle\Migration\QueryBag;
use RDV\Bundle\MigrationBundle\Migration\Extension\RenameExtension;
use RDV\Bundle\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",
        );
    }
}

每个包都可以有一个 安装 文件。此迁移文件替换了运行多个迁移文件。安装迁移类必须实现 Installation 接口,并必须实现 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 RDV\Bundle\MigrationBundle\Migration\Installation;
use RDV\Bundle\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']);
    }
}

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

此命令支持一些附加选项

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

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

此命令支持一些附加选项

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

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

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

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

数据库结构迁移的扩展

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

<?php

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

use Doctrine\DBAL\Schema\Schema;
use RDV\Bundle\MigrationBundle\Migration\Migration;
use RDV\Bundle\MigrationBundle\Migration\QueryBag;
use RDV\Bundle\MigrationBundle\Migration\Extension\RenameExtension;
use RDV\Bundle\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,您的迁移类应实现RenameExtensionAwareInterface接口和setRenameExtension方法。此外,您还可以在迁移类中使用一些其他有用的接口。

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

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

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

namespace Acme\Bundle\TestBundle\Migration\Extension;

use Doctrine\DBAL\Schema\Schema;
use RDV\Bundle\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: rdv_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命令时都会运行。

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

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

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

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

版本化固定

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

为了使设备版本化,此设备必须实现VersionedFixtureInterface接口。方法getVersion返回设备数据的版本,而getLoadedVersion返回当前加载的设备的版本。

示例

<?php

namespace Acme\DemoBundle\Migrations\Data\ORM;

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

use RDV\Bundle\MigrationBundle\Fixture\VersionedFixtureInterface;

class LoadSomeDataFixture extends AbstractFixture implements VersionedFixtureInterface
{
    /**
     * @var string
     */
    protected $loadedVersion;
    
    /**
     * {@inheritdoc}
     */
    public function getVersion()
    {
        return '1.0';
    }
    
    /**
     * {@inheritdoc}
     */
    public function setLoadedVersion($version = null)
    {
        $this->loadedVersion = $version;
    }

    /**
     * {@inheritdoc}
     */
    public function load(ObjectManager $manager)
    {
        // Here we can use fixture data code which will be run time after time
        
        if ($this->loadedVersion === null) { // loadedVersion is null for first time
        }
    }
}

在此示例中,如果设备尚未加载,它将被加载,并将版本1.0保存为当前加载的此设备版本。

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