ECCUBE3カスタマイズについていくつかのこと:既存テーブルへの拡張は回避して新規テーブルをプラグイン内に作れ

前置き

ECサイト構築においてECCUBEここ10年単位ではわりとメジャーな選択肢であり続けたと思いますけども、ECCUBE3系に移行してsymfonyベースに切り替わってからはいろいろと勝手が違って戸惑うこともしばしば。

エンティティ、リポジトリ | EC-CUBE 開発ドキュメント

既存テーブルに対する拡張

基本的に既存テーブルに対してプラグインからカラムを追加するような拡張は推奨していません。

既存テーブルに対して、例えばdtb_customerテーブルにニックネームを追加したい時は、プラグイン側でplg_profileというようなテーブルを作成して関連付けをします。

  • plg_profile

かつてはECCUBEカスタマイズに際してはさして強力なフレームワークガイドラインも無かったため、本体のコードに直接手を入れて改修するということが多く行われてきた。それはそれで自由に手が入れられる気安さはあったが同時にバージョンアップの阻害要因となるなどデメリットも多く、3.x系になってsymfony/doctrineをコア部分に据えてからはプラグインという形でのカスタマイズにパラダイムをシフトさせることとなった。その結果がこれである。

まあ、仕方ないといえば仕方ないのだが、たとえば商品テーブルにひとつカラムを追加するだけでも大きく遠回りをしなければいけなくなって多少しんどくはある。

そんなわけでここではとりあえず「SamplePlugin」というほぼスケルトン状態のプラグインを作成し、そこにエンティティ1個だけを追加した状態まで持っていき、実際にどんな作業になるかを振り返ってみる。「プラグインジェネレータの利用方法 | EC-CUBE 開発ドキュメント」と、「エンティティ、リポジトリ | EC-CUBE 開発ドキュメント」ページの「エンティティファイルの配置場所」の項目を見ながら進めるよ。というかECCUBE3のプラグインについて、情報はそこそここの公式サイトに載っているようなんだけどもなぜか目的に沿ってまとめられてなくてあちこちに情報の欠片が散乱している。どうにもかゆいところに手が届かないかんじ。サイトの構成自体はこれはこれで整理されているように見えるけど、使う側の目線も少しは気にしてくれよう。

プラグインの生成

$ cd /var/www/html/eccube-3.0.16 # ←eccubeのディレクトリ
$ php app/console plugin:develop generate
------------------------------------------------------
---Plugin Generator
---[*]You can exit from Console Application, by typing quit instead of typing another word.
------------------------------------------------------

[+]Please enter Plugin Name
Input[1] : サンプルプラグイン
[+]Please enter Plugin Code (First letter is uppercase alphabet only. alphabet and numbers are allowed.)
Input[2] : SamplePlugin
[+]Please enter version (correct format is x.y.z)
Input[3] : 0.1.0
[+]Please enter author name or company
Input[4] : zvorak.oweleo
[+]Do you want to support old versions too? [y/n]
Input[5] : n
[+]Please enter site events(you can find documentation here http://www.ec-cube.net/plugin/)
Input[6] : eccube.event.app.request
--- your entry list
 - eccube.event.app.request

--- Press Enter to move to the next step ---
[+]Please enter site events(you can find documentation here http://www.ec-cube.net/plugin/)
Input[6] :
[+]Please enter hookpoint, sample:front.cart.up.initialize
Input[7] : front.cart.up.initialize
--- your entry list
 - front.cart.up.initialize

--- Press Enter to move to the next step ---
[+]Please enter hookpoint, sample:front.cart.up.initialize
Input[7] :
[+]Would you like to use orm.path? [y/n]
Input[8] : y

---Entry confirmation
[+]Plugin Name:  サンプルプラグイン
[+]Plugin Code:  SamplePlugin
[+]Version:  0.1.0
[+]Author:  zvorak.oweleo
[+]Old version support:  No
[+]SiteEvents:
  eccube.event.app.request
[+]hookpoint:
  front.cart.up.initialize
[+]Use orm.path:  Yes

[confirm] Do you want to proceed? [y/n] : y

[+]File system

 this files and folders were created.
 - /var/www/html/eccube-3.0.16/app/Plugin/SamplePlugin
 - /var/www/html/eccube-3.0.16/app/Plugin/SamplePlugin/ServiceProvider
 - /var/www/html/eccube-3.0.16/app/Plugin/SamplePlugin/Controller
 - /var/www/html/eccube-3.0.16/app/Plugin/SamplePlugin/Form/Type
 - /var/www/html/eccube-3.0.16/app/Plugin/SamplePlugin/Resource/template/admin
 - /var/www/html/eccube-3.0.16/app/Plugin/SamplePlugin/config.yml
 - /var/www/html/eccube-3.0.16/app/Plugin/SamplePlugin/PluginManager.php
 - /var/www/html/eccube-3.0.16/app/Plugin/SamplePlugin/ServiceProvider/SamplePluginServiceProvider.php
 - /var/www/html/eccube-3.0.16/app/Plugin/SamplePlugin/Controller/ConfigController.php
 - /var/www/html/eccube-3.0.16/app/Plugin/SamplePlugin/Controller/SamplePluginController.php
 - /var/www/html/eccube-3.0.16/app/Plugin/SamplePlugin/Form/Type/SamplePluginConfigType.php
 - /var/www/html/eccube-3.0.16/app/Plugin/SamplePlugin/Resource/template/admin/config.twig
 - /var/www/html/eccube-3.0.16/app/Plugin/SamplePlugin/Resource/template/index.twig
 - /var/www/html/eccube-3.0.16/app/Plugin/SamplePlugin/event.yml
 - /var/www/html/eccube-3.0.16/app/Plugin/SamplePlugin/SamplePluginEvent.php
 - /var/www/html/eccube-3.0.16/app/Plugin/SamplePlugin/LICENSE

[+]Database
 Plugin information was added to table [DB.Plugin] (id=3)

 Plugin information was added to table [DB.PluginEventHandler] (inserts number=1)
Plugin was created successfully

詳しくは「プラグインジェネレータの利用方法 | EC-CUBE 開発ドキュメント」を見ていただくとして。

  • [1]はプラグイン一覧で表示される表示名。判別しやすい名前を付けておく。日本語で構わない。
  • [2]はプラグインをシステムが識別する際のIDとなる文字列。先頭文字が英大文字で他は英数文字であればよい。
  • [3]は最初のバージョン番号。開発者が管理できれば特に制約はない。
  • [4]は開発者または企業の名前を入れる。
  • [5]はECCUBEの古いバージョン(3.0.9より前)をサポートするか?ということなのだが、上記の入力値では旧バージョン向けコードが特に出てこず「y」でも「n」でも同じコードしか生成されないようなのでとりあえず無視する。というか積極的にサポートするつもりがない限りこれから新造するプラグインは「n」でいいと思う。
  • [6]はサイトイベントという、ECCUBE3でカスタマイズをする際に拡張処理を差し込む場所を指定するための入力値。後からでも追加できるので今はとりあえず仮の値として入力。(空行を入力するまで複数登録できる)
  • [7]は上に似てしかし若干ことなる仕組みでフックポイントという、これもまたECCUBE3をカスタマイズする際の拡張処理の差し込み場所になる。同じく後からの追加に任せて今は仮の値。(同じく複数登録可能)
  • [8]はorm(ORマッパ、doctrineのこと)を使用するので「y」を入力。というかまあ「ormを使用しない」とよほど自信をもって答えられるのでない限り基本は「y」を入力しておく方がいいでしょう。

プラグインエンティティの生成

次はプラグインが持つエンティティの生成。先にymlを作成してこれをもとにエンティティクラスやレポジトリクラスを生成する流れで進める。

$ vim app/Plugin/SamplePlugin/Resource/doctrine/Plugin.SamplePlugin.Entity.ProductReview.dcm.yml
Plugin\SamplePlugin\Entity\ProductReview:
    type: entity
    table: plg_sampleplugin_producctreview
    repositoryClass: Plugin\SamplePlugin\Repository\ProductReviewRepository
    id:
        id:
            type: integer
            nullable: false
            id: true
            column: product_id
    fields:
        comment:
            type: text
            nullable: true
    lifecycleCallbacks: {}

この.dcm.ymlで注意するのは以下の点。

  • idカラム(product_id)は既存テーブルdtb_productのIDと一致を保つためauto incrementは入れない。
  • 関連してforeign key設定も行いたいところだがこれはTODOとして次回以降に。
  • プロダクト(dtb_product)一個に対して拡張コメント(plg_sampleplugin_productreview)一個というリレーション。
  • ファイル名は「Plugin.[プラグインコード].Entity.[エンティティ名].dcm.yml」のパターンを踏襲しておく。

ここまで出来たら、エンティティやレポジトリ等のコード生成に進行。

$ php app/console plugin:develop entity

[entity]How to generate entities from db schema or yml? [d => db, y => yml] : y
------------------------------------------------------
---Plugin Generator for Entity
---[*]You need to create yml file first.
---[*]You can exit from Console Application, by typing quit instead of typing another word.
------------------------------------------------------

[+]Please enter Plugin Code (First letter is uppercase alphabet only. alphabet and numbers are allowed.)
Input[1] : SamplePlugin
[+]Plese enter yml file name
Input[2] : Plugin.SamplePlugin.Entity.ProductReview.dcm.yml
--- your entry list
 - Plugin.SamplePlugin.Entity.ProductReview.dcm.yml

--- Press Enter to move to the next step ---
[+]Plese enter yml file name
Input[2] :
[+]Do you want to support old versions too? [y/n]
Input[3] : n

---Entry confirmation
[+]Plugin Code:  SamplePlugin
[+]Yml file name:
  Plugin.SamplePlugin.Entity.ProductReview.dcm.yml
[+]Old version support:  No

[confirm] Do you want to proceed? [y/n] : y

[+]File system

 this files and folders were created.
 - /var/www/html/eccube-3.0.16/app/Plugin/SamplePlugin/Entity
 - /var/www/html/eccube-3.0.16/app/Plugin/SamplePlugin/Repository
 - /var/www/html/eccube-3.0.16/app/Plugin/SamplePlugin/Resource/doctrine/migration
 - /var/www/html/eccube-3.0.16/app/Plugin/SamplePlugin/Entity/ProductReview.php
 - /var/www/html/eccube-3.0.16/app/Plugin/SamplePlugin/Repository/ProductReviewRepository.php
 - /var/www/html/eccube-3.0.16/app/Plugin/SamplePlugin/Resource/doctrine/migration/Version20180521190045.php
  • [1]はプラグインのコード名。今回なら「SamplePlugin」。
  • [2]は直前に生成したyamlのファイル名を入力。これも空行を入れるまで複数登録が可能。
  • [3]はこれもプラグインコード生成時と同じく古い(3.0.9よりも前)ECCUBE3バージョンへの対応要否。積極的にサポートする意思や要請がないのであれば基本「n」でよい。

ここまで来たらマイグレーションを使ってテーブル作成ができるので試してみる。

$ php app/console plugin:develop uninstall --code SamplePlugin
$ php app/console plugin:develop install --code SamplePlugin
$ php app/console plugin:develop enable --code SamplePlugin

追加されたエンティティをECCUBEが認識するためには一度アンインストールする必要があるようなので注意。そうしないとエンティティの捜索パスから新しいエンティティを見つけることができずエラーになるようだ。enableまで出来たらDBの中身を確認しよう。

MariaDB [eccube3]> SHOW TABLES LIKE 'plg_%';
+---------------------------------+
| Tables_in_eccube3 (plg_%)       |
+---------------------------------+
| plg_sampleplugin_producctreview |
+---------------------------------+
1 row in set (0.00 sec)

MariaDB [eccube3]> EXPLAIN plg_sampleplugin_producctreview;
+------------+----------+------+-----+---------+-------+
| Field      | Type     | Null | Key | Default | Extra |
+------------+----------+------+-----+---------+-------+
| product_id | int(11)  | NO   | PRI | NULL    |       |
| comment    | longtext | YES  |     | NULL    |       |
+------------+----------+------+-----+---------+-------+
2 rows in set (0.00 sec)

これでプラグイン内に拡張用エンティティを保持する仕組みを用意することができた。PHPコード上でどのように扱うかは次回以降のエントリに任せてとりあえず今日はここまで。