PHPで「Doctrine Migrations」を使ってみる
LaravelやCakePHPなど有名どころのフレームワークを使用していないPHP製のWEBアプリケーションでDBスキーマのバージョン管理ができるMigrationツールとしてDoctrine Migrations、Phinxがあります。
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
というファイルでマイグレーションクラスを作成してくれたようなのでさっそく確認します。
<?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テーブルの定義を書いてみます。
<?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クラスインスタンスによる表現も可能です。
<?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