2019年5月23日木曜日

EC-CUBE4でプラグインからのマイグレーションでEntityManager・Entityを使う方法

SYSTEM_KDです。


今回のブログから、StackEdit を利用してMarkdownで書いてみています。
(いや、何の宣言?


久々の投稿ですが、内容はEC-CUBE4でのマイグレーションについてです。


ほぼメモです。



EC-CUBE4でのプラグインからマイグレーション


EC-CUBE3では、プラグインでテーブルの拡張・データ追加を行う際はPluginMigrationで行いますが、
4系ではテーブルの拡張は「doctrine:schema:update」での拡張が推奨されてます。

(まぁ、プラグインからのテーブル拡張は、プラグイン機構部分が良しなにしてくれるのであまり気にしなくてOK)


データの追加を行う際も、PluginMigrationから行えます。

AbstractPluginManager を継承して enable() 関数にてinsertするなり、$container(ContainerInterface) からEntityManagerを利用するなりしてデータ追加が行えます。


ただ、この方法だと再度 enable()が操作した際にinsertしないようにチェックする必要があります。

プラグインをバージョンアップして、追加するデータが増えた際・既に追加しているデータを変更する際など、管理が面倒くさい。


振り返らずに、前に進みたいです。


ということで、3系と同様にマイグレーションファイルを用意して、追加するデータを管理したいと思います。


必要なファイル
(プラグイン用ディレクトリの中)
  • PluginManager.php
  • DoctrineMigrations/Version2019XXXXXXXXXX.php


    VersionXXXのファイルは、下記コマンドで生成
    (生成できたらプラグインの中に持ってくる)
bin/console doctrine:migrations:generate

PluginManager.php は、Eccube\Plugin\AbstractPluginManagerを継承する。

AbstractPluginManagerにある、migrationメソッドを呼べば3系の時みたいにバージョン管理しながらデータ追加が行える。


有効にした際にデータ追加したい場合
public function enable(array $meta, ContainerInterface $container)  
{  
    $this->migration($meta['code'], $container);
}

逆にアンインストールで削除する場合
public function uninstall(array $meta, ContainerInterface $container)  
{  
    $this->migration($meta['code'], $container, 0);  
}


あとは、マイグレーションファイル内の up() 関数で、`addSql`すれば良い。
※アンインストール時は、down()

(色々とサンプルが転がっているのでざっくりで m(_ _)m


でも、これだとEntityManager使えない。Entityが使えない。。使いたい。。


で、本題のEntityManagerを使ってのマイグレーションです。

マイグレーションファイルで、ContainerAwareInterfaceをimplementsし、
use ContainerAwareTraitを差し込んであげれば、ContainerInterfaceを使えるようになると見せかけて、ならない。

bin/console doctrine:migrations:migrate

で、app/DoctrineMigrations にあるマイグレーションファイルは良い感じになりますが、プラグインの方はダメっぽい。

やり方が悪いだけだったりして・・・^^;


解決方法
Eccube\Plugin\AbstractPluginManagermigration 関数をベースにちょっと書き換えます。

PluginManager .php
class PluginManager extends AbstractPluginManager  
{
 /**  
  * @param array $meta  
  * @param ContainerInterface $container  
  * @throws MigrationException  
  */
 public function uninstall(array $meta, ContainerInterface $container)  
 {  
     $this->pluginMigration($meta, $container, 0);  
 }  
   
 /**  
  * * @param array $meta  
  * @param ContainerInterface $container  
  * @throws MigrationException  
  */
 public function enable(array $meta, ContainerInterface $container)  
 {  
     $this->pluginMigration($meta, $container);  
 }

 protected function pluginMigration(array $meta, ContainerInterface $container, $version = null)  
 {
     $migrationFilePath = __DIR__ . '/DoctrineMigrations';  
     $connection = $container->get('database_connection');  
   
     $pluginCode = $meta['code'];  
   
     $config = new Configuration($connection);  
     $config->setMigrationsNamespace('\Plugin\\' . $pluginCode . '\DoctrineMigrations');  
     $config->setMigrationsDirectory($migrationFilePath);  
     $config->registerMigrationsFromDirectory($migrationFilePath);  
     $config->setMigrationsTableName(self::MIGRATION_TABLE_PREFIX . $pluginCode);  
   
     /** @var Version $objVersion */  
     foreach ($config->getMigrations() as $objVersion) {  
         $versionMigration = $objVersion->getMigration();  

         if ($versionMigration instanceof ContainerAwareInterface) {  
             $versionMigration->setContainer($container);  
         }  
     }
   
     $migration = new Migration($config);  
     $migration->migrate($version, false);  
 }
}

間違え探しみたいな微妙な違いですが、、
キモとなる部分はこの部分になります。

ContainerInterfaceをVersionXXX側に渡してあげるようにします。
/** @var Version $objVersion */  
foreach ($config->getMigrations() as $objVersion) {  
    $versionMigration = $objVersion->getMigration();  

    if ($versionMigration instanceof ContainerAwareInterface) {  
        $versionMigration->setContainer($container);  
    }  
}


これで無事にマイグレーションをバージョン管理しながら、EntityManager・Entityを使うことができます。


final class VersionXXX extends AbstractMigration implements ContainerAwareInterface  
{
    use ContainerAwareTrait;

    public function up(Schema $schema): void  
    {  
        /** @var EntityManagerInterface $entityManager */  
        $entityManager = $this->container->get('doctrine.orm.entity_manager');  
   
        $ProductRepository = $entityManager->getRepository(Product::class);
    }  
   
    public function down(Schema $schema): void  
    {  
   
    }
}


まとめ


bin/console でマイグレーションする場合は、VersionXXXの方だけで良いけど、
EC-CUBE4のプラグインでマイグレーションする際は、PluginMigrationでContainerInterfaceを渡してあげる必要がある。