🎓

PHPで「Doctrine Migrations」を使ってみる

2023/09/03に公開

LaravelやCakePHPなど有名どころのフレームワークを使用していないPHP製のWEBアプリケーションでDBスキーマのバージョン管理ができるMigrationツールとしてDoctrine MigrationsPhinxがあります。

DoctrineはSymfonyでも採用されているORMですが単体でも利用可能なようです。
PhinxはCakePHPの公式マイグレーションツールですが、こちらも独立したコマンドラインツールとして利用できるようですね。

執筆時点(2023/09/03)でGithubのスター数はそれぞれ「Doctrine Migrations」が4.6k、「Phinx」が4.4kでした。
今回は(自分用のメモとして)スターが多かった「Doctrine Migrations」の使い方を簡単に書き留めたいと思います。

導入方法

今回はComposerを利用します。
この記事ではプロジェクトルートを/var/www/applicationとしていますが、皆さんの環境に合わせて読み換えてください。

cd /var/www/application
composer require doctrine/migrations

するとvendor/bin配下にマイグレーションを実行できるファイルが作成されます。

./vendor/bin/doctrine-migrations

構成

マイグレーション構成

マイグレーションファイルを保存するディレクトリを作成します。

mkdir migrations

私はプロジェクトルートにmigrationsディレクトリを作成しました。プロジェクトルート配下であれば任意の階層、ディレクトリでOKのようです。

次にプロジェクトルートにmigrations.phpという名前のファイルを作成し、次の内容を記述します。

<?php

return [
    'table_storage' => [
        'table_name' => 'doctrine_migration_versions',
        'version_column_name' => 'version',
        'version_column_length' => 191,
        'executed_at_column_name' => 'executed_at',
        'execution_time_column_name' => 'execution_time',
    ],

    'migrations_paths' => [
        #Migrationクラスのnamespace
        'MyProject\Migrations' => '/var/www/application/migrations',
    ],

    'all_or_nothing' => true,
    'transactional' => true,
    'check_database_platform' => true,
    'organize_migrations' => 'none',
    'connection' => null,
    'em' => null,
];

このファイルはphpの他にyaml,xml,json形式でも記述が可能です。
yamlで書く場合はsymfony/yamlパッケージのインストールが必要です。

composer require symfony/yaml

接続構成

DBへの接続に関する情報を記述したmigrations-db.phpファイルをプロジェクトルートに設置します。

<?php

return [
    'dbname' => 'demo_db',
    'user' => '{user name}',
    'password' => '{user password}',
    'host' => '{db host}',
    'driver' => 'pdo_mysql',
];

マイグレーションファイルを作成する

ここまででDoctrine Migrationsを使用する準備が整いました。
続いてマイグレーションファイルを作成していきましょう。

./vendor/bin/doctrine-migrations generate

以下のようにCLIに出力されました。

 Generated new migration class to "/var/www/application/migrations/Version20230903021357.php"

 To run just this migration for testing purposes, you can use migrations:execute --up 'Demo\Migrations\Version20230903021357'

 To revert the migration you can use migrations:execute --down 'Demo\Migrations\Version20230903021357'

/var/www/application/migrations/Version20230903021357.phpというファイルでマイグレーションクラスを作成してくれたようなのでさっそく確認します。

migrations/Version20230903021357.php
<?php

declare(strict_types=1);

namespace Demo\Migrations;

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

/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20230903021357 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

   }
}

空のマイグレーションクラスが作成されています。
up()down()メソッドは最低限実装が必要です。
他にも環境に合わせて様々なオーバーライド可能なメソッドがあるようです。

試しに簡単なpostsテーブルの定義を書いてみます。

migrations/Version20230903021357.php
<?php

declare(strict_types=1);

namespace Demo\Migrations;

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

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

    public function up(Schema $schema): void
    {
        $query = <<< EOL
        CREATE TABLE posts  (
            id INT AUTO_INCREMENT NOT NULL,
            title VARCHAR(255) DEFAULT NULL,
            content TEXT DEFAULT NULL,
            created_at DATETIME DEFAULT NULL,
            updated_at DATETIME DEFAULT NULL,
            deleted_at DATETIME DEFAULT NULL
        )
        EOL;
        $this->addSql($query);
    }

    public function down(Schema $schema): void
    {
        $this->addSql("DROP TABLE posts");
    }
}

SQLを記述する以外にもschemaクラスインスタンスによる表現も可能です。

migrations/Version20230903021357.php
<?php

declare(strict_types=1);

namespace Demo\Migrations;

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

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

    public function up(Schema $schema): void
    {
        $table = $schema->createTable("posts");
        $table->addColumn("id", "INTEGER", [
            "autoincrement" => true,
            "notnull" => true,
        ]);
        $table->addColumn("title", "string", [
            "notnull" => false,
            "default" => null
        ]);
        $table->addColumn("content", "text", [
            "notnull" => false,
            "default" => null
        ]);
        $table->addColumn("created_at", "datetime", [
            "notnull" => false,
            "default" => null
        ]);
        $table->addColumn("updated_at", "datetime", [
            "notnull" => false,
            "default" => null
        ]);
        $table->addColumn("deleted_at", "datetime", [
            "notnull" => false,
            "default" => null
        ]);
        $table->setPrimaryKey(["id"]);
    }

    public function down(Schema $schema): void
    {
        $schema->dropTable("posts");
    }
}

laravelに慣れている人はこちらの方が親しみやすいかも知れません。

マイグレーションを実行する

作成したマイグレーションファイルを実行します。

./vendor/bin/doctrine-migrations migrate 
 WARNING! You are about to execute a migration in database "demo" that could result in schema changes and data loss. Are you sure you wish to continue? (yes/no) [yes]:
 > y

[notice] Migrating up to Demo\Migrations\Version20230903021357
[notice] finished in 32.1ms, used 6M memory, 1 migrations executed, 1 sql queries

 [OK] Successfully migrated to version : Demo\Migrations\Version20230903021357

無事にpostsテーブルが作成されました。

マイグレーションのロールバック

マイグレーションをロールバックするには以下のコマンドを実行します。

./vendor/bin/doctrine-migrations migrate prev
 WARNING! You are about to execute a migration in database "demo" that could result in schema changes and data loss. Are you sure you wish to continue? (yes/no) [yes]:
 >

[notice] Migrating down to Demo\Migrations\Version20230902154040
[notice] finished in 34ms, used 6M memory, 1 migrations executed, 1 sql queries

 [OK] Successfully migrated to version : Demo\Migrations\Version20230902154040

一旦ここまでとします。
他にも様々な方法でマイグレーションに関する機能や設定があるので、詳しくは公式ドキュメントを見ていただくと発見があるかもしれません。

Discussion