EC-CUBE4のドキュメントに書いてないプロキシ生成の挙動
この記事は、EC-CUBE Advent Calendar 2019 の記事になります。
明日は EC-CUBE社の chihiro-adachiさんによる「EC-CUBE2.xからEC-CUBE4.xへの移行方法」になります。
EC-CUBE4系では、コアのEntityを拡張する時に、TraitとDoctrineのannotationを使った拡張が推奨されています。
トレイトを使うと、拡張したスキーマのDBへの反映時にコマンドラインでプロキシ作って反映させるんですが、その際にちょっと想定していなかった挙動をしていてハマっtたのでご報告です。
プロキシはコアのEntityとプラグイン、カスタマイズ領域のTraitをがっちゃんこしたもの
例えば、 app/Customize/Entity に独自のフィールドやメソッドを追加したTraitを作って、プラグインインストールしてそれらを反映させるためにプロキシ生成コマンドを叩きます。この際、app/proxy 配下にはプロキシが生成されていないとします。
その場合、EC-CUBEは、 src/Eccube/Entity の中の該当のEntity(仮にProductEntityとします)を app/proxyにコピーし、Customize/Entity やプラグインのEntityをスキャンして対応するトレイトを見つけ、作成したプロキシクラスに追記します。
コアのProductEntity
<?php /* * This file is part of EC-CUBE * * Copyright(c) LOCKON CO.,LTD. All Rights Reserved. * * http://www.lockon.co.jp/ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Eccube\Entity; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\Mapping as ORM; if (!class_exists('\Eccube\Entity\Product')) { /** * Product * * @ORM\Table(name="dtb_product") * @ORM\InheritanceType("SINGLE_TABLE") * @ORM\DiscriminatorColumn(name="discriminator_type", type="string", length=255) * @ORM\HasLifecycleCallbacks() * @ORM\Entity(repositoryClass="Eccube\Repository\ProductRepository") */ class Product extends \Eccube\Entity\AbstractEntity { private $_calc = false; private $stockFinds = []; private $stocks = []; private $stockUnlimiteds = [];
生成されるプロキシ
<?php /* * This file is part of EC-CUBE * * Copyright(c) LOCKON CO.,LTD. All Rights Reserved. * * http://www.lockon.co.jp/ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Eccube\Entity; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\Mapping as ORM; /** * Product * * @ORM\Table(name="dtb_product") * @ORM\InheritanceType("SINGLE_TABLE") * @ORM\DiscriminatorColumn(name="discriminator_type", type="string", length=255) * @ORM\HasLifecycleCallbacks() * @ORM\Entity(repositoryClass="Eccube\Repository\ProductRepository") */ class Product extends \Eccube\Entity\AbstractEntity { use \Customize\Entity\ProductTrait, Plugin\BundleSale4\Entity\ProductTrait; private $_calc = false; private $stockFinds = []; private $stocks = []; private $stockUnlimiteds = [];
こんな感じです。
クラス定義の中でuse文を使ってtraitが使われています。
コアのEntityはプロキシを作る最初の1回しか見ない
バージョン(4.0.3)依存か環境依存の可能性もあるのですが、とりあえずこの時の僕の環境では、コア( src/Eccube/Entity )のファイルは、プロキシ初回生成時しか読まれませんでした。
と言うのも、どうしてもtraitでは対応できない処理をコアのEntityに追加したのですが、何度プロキシを生成し直したりキャッシュ消したりしてもそれが反映されなかったんです。
なんでだろう?と頭を捻ってキャッシュやら諸々見てみると、なんと app/proxy に生成されるプロキシにコアのEntityに追加した処理が反映されてなかったのです。
それ以外にtraitで追加したものはちゃんと含まれていたので、
traitの変化は追跡するけど、コアの変更は追跡しない
という仕様の様でした。
たまたま勉強会に参加されていたEC-CUBE社のCTOのkiyatakaさんに相談してみると、プロキシを一旦消せば再度コアを見て再生成されるはず、との事でしたが、プロキシ消すと「プロキシが見つかりません」という旨のエラーが出てダメでした。
バージョンアップの時に注意が必要
通常、コアのEntityを直接いじるというケースはあまりないと思うのですが、EC-CUBE自体のバージョンアップ時にはコアのEntityが変わる可能性があります。
その場合、カスタマイズしていたりプラグインをインストールしていたりすると、手動でgithubとかから差分持ってきてコアをバージョンアップした際にコアの変更が反映されない可能性があります。
kiyotakaさんに内容は伝えてあるので、いずれ修正されるとは思いますが、Entity拡張してカスタマイズとかしていて変な挙動に遭遇したらこの記事を思い出してください。
僕は3日ハマりました...