EC-CUBE 4で新しいテーブルを追加した時、本番に流すCREATE TABLE文はeccube:schema:update --dump-sqlで取るのが基本です。ところがこのコマンドはエンティティとDBの差分を全部出すため、他のブランチ由来のカラム削除や、まだ反映しないテーブルの文が混ざることがあります。欲しいテーブルの文だけを取り出すには、DoctrineのSchemaToolに対象のエンティティだけを渡します。
dump-sqlをそのまま使えない場面
--dump-sqlの出力は「今のエンティティ定義と、今接続しているDBの差分」です。ローカルのDBが少し古かったり、別のブランチで追加したカラムがエンティティ側に残っていたりすると、目的のCREATE TABLEの隣にALTER TABLE ... DROPが並びます。私のケースでは、リリース対象の2テーブルに加えて、別のブランチで追加した会員テーブルのカラムを削除する文が出てきました。
この状態で--forceを付けると全部適用されるので使えません。出力から必要な行だけを手でコピーする方法もありますが、行の見落としや外部キーの取り違えが起きます。本番ではbin/consoleを実行せず、SQLファイルを流す運用にしているなら、なおさら「対象だけの正確な文」が欲しくなります。
SchemaToolに対象のClassMetadataだけを渡す
--dump-sqlが内部で使っているDoctrine\ORM\Tools\SchemaToolは、渡したClassMetadataの配列に対するCREATE文を返すメソッドを持っています。カーネルを起こしてEntityManagerを取り、対象クラスのメタデータだけを渡します。EC-CUBE 4.0(Symfony 3.4)で動かしたスクリプトは次のとおりです。
<?php
// 対象エンティティのCREATE TABLE文だけを出力する
require '/path/to/ec-cube/vendor/autoload.php';
(new Symfony\Component\Dotenv\Dotenv())->load('/path/to/ec-cube/.env');
$kernel = new Eccube\Kernel('dev', true);
$kernel->boot();
$em = $kernel->getContainer()->get('doctrine.orm.entity_manager');
$tool = new Doctrine\ORM\Tools\SchemaTool($em);
$classes = [
$em->getClassMetadata(Customize\Entity\LineRichMenuLink::class),
$em->getClassMetadata(Customize\Entity\LineRichMenuClickLog::class),
];
foreach ($tool->getCreateSchemaSql($classes) as $sql) {
echo $sql, ";\n";
}
出力は--dump-sqlと同じ書式で、対象の2テーブル分だけが出ます。
CREATE TABLE dtb_line_rich_menu_link (id INT UNSIGNED AUTO_INCREMENT NOT NULL, code VARCHAR(64) NOT NULL, ... PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE = InnoDB;
CREATE TABLE dtb_line_rich_menu_click_log (id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, line_rich_menu_link_id INT UNSIGNED NOT NULL, ... PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE = InnoDB;
ALTER TABLE dtb_line_rich_menu_click_log ADD CONSTRAINT FK_607A86AB1F952B45 FOREIGN KEY (line_rich_menu_link_id) REFERENCES dtb_line_rich_menu_link (id);
ローカルで--dump-sql経由で作っておいたテーブルのSHOW CREATE TABLEと突き合わせたところ、カラム定義・インデックス名・外部キー名まで一致していました。DBの状態に依存せず、エンティティ定義だけから文を作っているので、この一致は当然の結果です。
外部キーは別のALTER文で出る
出力を見ると、外部キー制約はCREATE TABLEの中ではなく、最後にALTER TABLE ... ADD CONSTRAINTとして出てきます。Doctrineは参照先のテーブルが存在してから制約を張る順序で文を組むためです。SQLファイルにまとめる時は、この順序を崩さずにそのまま並べます。制約名(FK_607A...)はテーブル名とカラム名から決まるハッシュなので、後で--dump-sqlを実行しても差分として検知されません。
実行時に引っかかった点
スクリプトはbin/consoleと同じくカーネルを起動するので、サービスコンテナのコンパイルが実行されます。私の環境ではPHPのmemory_limitが128MBのままだとメモリ不足で止まったため、php -d memory_limit=1Gを付けて実行しました。カーネルの第2引数(debug)をtrueにしておくと、通常のdev環境と同じキャッシュディレクトリを使うため、既にコンパイル済みのコンテナがそのまま再利用されます。
EC-CUBEに関するお問い合わせ
[重要]現在公式にセキュリティサポートが切れていないPHPは8.1以上、MySQLは8.0以上で、対応しているEC-CUBEバージョンは4.2以上です。古いEC-CUBEを使っている方は適切なタイミングでバージョンアップをご検討ください。