2017年以前の旧ブログ

BLOG2017

EC-CUBE3で本体のRepositoryをカスタマイズ

OB・OG Other Blog

どうも、バンチです。
最近EC-CUBEの世界にどっぷり浸かっています。
ちょっと個人的につまった箇所があったので、技術メモとして残しておきます。

Repositoryをカスタマイズしたい

たとえば、
 商品一覧の抽出条件を変更したい(※)
 効率悪いクエリーを差し替えたい
なんかを実現したい場合、手っ取り早いのは本体のコードを直接変更してしまう方法です。
※単純に条件追加したいだけならフックポイントとかフィルタ機能とか使うのが正解です。

しかし、EC-CUBEのカスタマイズ指針としては本体のコードはさわらず、「プラグイン」の仕組みを使うことが推奨されています。
今後の本体のバージョンアップとか無視するのであれば触ってしまってもいいのですが、セキュリティ対策のためのアップデート等を考えると極力避けたいところです。

なので、本体のソースは触らずにRepositoryを差し替える方法を試行錯誤したのでした。

継承クラス

Repository/Entityの継承クラスを作って、それでコンテナの中身差し替えてしまえばいいんじゃ?
まず思いついたのがこれです。

試したらさくっと動いたので終了!とおもったら対象のテーブルへの更新クエリーがことごとく失敗します。
どうやらEntityを継承させることがORM側の継承マッピング扱いになるようで、別テーブルが存在するかのようなクエリーを組み立ててしまってます。

Repositoryだけ差し替え

Entity/Repositoryの紐付けはdoctrineの定義で行っているため、Repositoryを新しくつくるのであればEntityもセットである必要があるのかと思い込んでいました。
だからか、この方法が思いつくまでの壁が厚かった・・・

具体的にはこうします。 ※商品(ProductRepository)を差し替える場合

$app['eccube.repository.product'] = $app->share(function () use ($app) {
    //$productRepository = $app['orm.em']->getRepository('Eccube\Entity\Product'); // 参考)本体での定義
    //$productRepository = $app['orm.em']->getRepository('Plugin\OriginalPlugin\Entity\Product'); //これはNG
    $productRepository = new OriginalRepository($app['orm.em'], $app['orm.em']->getMetadataFactory()->getMetadataFor('Eccube\Entity\Product'));
    return $productRepository;
});

通常はマッピング情報に含まれるRepository定義を参照してインスタンスを引っ張ってくるのですが、直接オリジナルのリポジトリをnewします。
初期化に必要なマッピング情報は元のRepositoryと同じものを直接指定しています。
OriginalRepositoryは元の\Eccube\Repository\ProductRepositoryを継承させているので、本体や各種プラグインで商品リポジトリを参照する処理があっても、問題なくOriginalRepositoryがProductRepositoryに置き換わって動作します。

きっとSymphony2&Doctrineをちゃんと知ってる人からみれば当たり前の話だとは思いますが、なにぶんググっても情報でてこなくて・・・

あと、そこまでするなら本体のコードを書き換えたほうがいいです。
本体のアップデートの影響を直接はうけませんが、結局上書き対象の処理に変更があればOriginal側にも反映しないと辻褄があわなくなります。

Entityの拡張は?

これは多分無理・・・じゃないかな。
自分には良いアイデアが思いつかなかったです。