EC-CUBE開発情報

EC-CUBE プラグイン 作り方 その3 (処理のフック)

2013/07/10
 : S.USUI

ここまでソースを実装してみてお気づきかと思いますが
EC-CUBEでは基盤となる既存処理クラスを実行する場合も必ず_Exという処理クラスを一つ継承した先のクラスのメソッドを実行するように実装されています。
これは、カスタマイズする際に直接処理を書き加えたり、変更するのではなく継承先のクラスをオーバーライドして処理を差し替えることで、基盤となる処理とカスタマイズ処理を分離するためです。
この方法での実装が徹底されているば、カスタマイズ後でも、基盤クラスを差し替えればバージョンアップすることができます。
直接処理クラスを書き換えて変更してしまった場合は、どこまでがカスタマイズでどこからが既存処理かわからなくなってしまいソースをマージすることが困難になります。
 
ただし、この方法では既存処理に対してオーバーライドできるのは1回だけとなり、複数の機能から同じ処理について追加処理を記述するとができません。
そのためプラグイン開発では、フックポイントが用意され、既存処理の前後に処理を追記する指定が可能となっています。LC系クラスであればactionのbeforeとafterが用意されておりコールバックに指定した関数を実行させることが可能です。SC系クラスはloadClassFileChangeで対象クラス自体を差し替えることが可能です。
 
もし同じフックポイントが利用されているプラグインをインストールした場合は、
プラグイン管理の画面で競合する可能性がありますと警告が表示されます。
201307091051_1-600x0.png
 
同じフックポイントが指定されている場合は管理画面の優先度の順番で処理が実行されます。
プラグインAの処理を実行後、プラグインBの処理を実行するなどの設定が可能です。
LC系クラスのフックであれば順番を考慮してインストールすれば共存は可能ですが
SC系の処理をフックするところで競合が発生した場合は実質共存は難しいのかと思います。
自分で作成するプラグインが他プラグインと競合してしまう場合も十分ありますので
プラグインを作成する場合は必ず、アンインストールしたときに処理が元の状態に戻せるように気を使いましょう。
 
さて本題にもどり、今回のプラグインでは商品を参照した時にcookie書き込む処理を追加したいので
利用可能なフックポイントの一覧から対象のフックポイントを確認します。
 
対象ページのURLは/products/detail.phpなのでファイルの命名ルールより
LC_Page_Products_Detail_action_afterを利用するれば目的をはたせそうです。
 
さっそく処理をRecentAccessBlock.phpに追記していきます。
RecentAccessBlock::registerメソッドにフックポイントで実行させたい処理を追記します。
function register(SC_Helper_Plugin $objHelperPlugin) {
  // フックポイントの設定
  $objHelperPlugin->addAction('LC_Page_Products_Detail_action_after', array(&$this, 'setRecentAccessProductsCookie'));
}
 
実行したいメソッドをRecentAccessBlock::setRecentAccessProductsCookieとしたので処理内容を追記します。
function setRecentAccessProductsCookie($objPage) {
  // Cookieから最近見た商品情報を取得
  $objCookie = new SC_Cookie_Ex(COOKIE_EXPIRE);
  $old_product_ids = $objCookie->getCookie('recent_access_products');

  // プラグイン情報を取得
  $plugin = SC_Plugin_Util_Ex::getPluginByPluginCode("RecentAccessBlock");
  $productsCount = (isset($plugin['free_field1']) && $plugin['free_field1'] > 0) ?
  (int)$plugin['free_field1']: self::DEFAULT_DISP_PRODUCTS_COUNT;

  // 表示中の商品IDを取得
  $product_id = $objPage->tpl_product_id;
  if ($product_id){
    $new_product_ids = array();
    $new_key = 0;
    // Cookieに現在の商品Idを保存
    $objCookie->setCookie('recent_access_products', null);
    $objCookie->setCookie('recent_access_products['.$new_key.']', (int)$product_id);
    // 最近みた商品の履歴を1つシフトする
    if (($productsCount > 1) && isset($old_product_ids)){
      foreach ($old_product_ids as $key => $id){
        if ($id != $product_id){
          $new_key += 1;
          // Cookieに最近見た商品Idを保存
          $objCookie->setCookie('recent_access_products['.$new_key.']', (int)$id);
          if ($new_key == ($productsCount - 1)) {
            break;
          }
        }
      }
    }
  }    	
}
 
プラグイン情報から取得する値の初期値をRecentAccessBlock::constとして定義
  //ブロックに表示する商品数(DEFAULT)
  const DEFAULT_DISP_PRODUCTS_COUNT = 4;
 
追加する処理としては以上です。
ためしに実行されているかログをしこんでみると
GC_Utils_Ex::gfFrontLog("Cookie set hear (".__LINE__.")");
アクセスすると実行されているのがわかります。
\\eccube-2.12.3\data\downloads\plugin\RecentAccessBlock\RecentAccessBlock.php(129): GC_Utils->gfFrontLog
\\eccube-2.12.3\data\class\util\GC_Utils.php(163): GC_Utils->gfPrintLog
2013/07/09 12:14:56 [/eccube/products/detail.php] Cookie set hear (129) from ::1
customer_id = 2
さらに、chromeのインスペクタなどでcookieの状態を確認してみると。。。
アクセスの度にカウントアップされてます!
201307091222_1-600x0.png
これで参照履歴をデータに記録する部分が実装できました。
 
ソースを直接変更しながら実装をすすめてきましたが、本来はEC-CUBEのプラグインインストール時にregisterに記述してある内容より、dtb_plugin_hookpointテーブルにフックポイントで実行するメソッドのデータがインサートされます。
pluginの競合の管理などに利用されているデータで、公開用のモジュールを作成する時まで未作成のまま放置しておいてもかまいませんが、今回は直接SQLでデータを追加してみます。
 
EC-CUBEのテーブル構造で自動発番のidについては実データのテーブルカラムにautoincrementを設けるのではなく、カラム名と同じ名称シーケンス用のテーブルを別途用意してインクリメントしているパターンが多いです。
ここはまりやすいのですが、パッと見はシーケンス用のテーブルのレコードの値を利用してインクリメントしているようにみえますが、実際にはシーケンス用のテーブルのautoincrementの値を利用しており、シーケンス用のテーブルのレコードの値はautoincrementの値を保持するためのダミーレコードです。
 
今回対象としているテーブルは
dtb_plugin_hookpoint(データテーブル)、dtb_plugin_plugin_id_seq(シーケンス用テーブル)となりますので
 
現在のシーケンスを取得
SHOW TABLE STATUS LIKE 'dtb_plugin_plugin_id_seq';
 
データ追加
INSERT INTO  dtb_plugin_hookpoint (plugin_hookpoint_id, plugin_id, hook_point , callback, create_date, update_date) 
VALUES ( '999',  '99999',  'LC_Page_Products_Detail_action_after',  'setRecentAccessProductsCookie', now() , now());
 
シーケンスのインクリメント
UPDATE dtb_plugin_plugin_id_seq SET =99999;
ALTER TABLE dtb_plugin_plugin_id_seq autoincrement= 100000;
 
次回はいよいよ最終回、商品履歴をブロックに表示する部分を紹介します。
 

連載:最近チェックした商品の履歴プラグインをつくってみました。
 EC-CUBE プラグイン 作り方 その1
 EC-CUBE プラグイン 作り方 その2
 EC-CUBE プラグイン 作り方 その3