/data/class/page/products/LC_Page_Products_Buy.phpから。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
|
require_once(CLASS_PATH .
"pages/frontparts/bloc/LC_Page_FrontParts_Bloc.php");
class LC_Page_Products_Buy extends LC_Page_FrontParts_Bloc {
//定数
const ERROR_MESSAGE = '買い物籠への投入処理に失敗しました';
//変数
protected $error_flg=false;
//初期化
public function init() {
parent::init();
//テンプレは使用しません
//DB接続クラス
$this->objDb = new SC_Helper_DB_Ex();
}
//デストラクタ.
public function destroy() {
parent::destroy();
}
//メイン処理
public function process() {
//引数から、商品番号および件数を取得
$item_array=$this->getRequest($_REQUEST);
if(!$item_array){
$this->printError();
exit();
}
//商品をカートに投入
$ret=$this->setOrder($item_array);
if($this->error_flg){
$this->printError();
exit();
}
//買い物が成功
$this->processSuceed();
exit();
}
//---------------------------------------------------
// 商品IDから商品リストを作成する
protected function getRequest($request){
//返り値
$ret=array();
//繰り返し
foreach($request['product_id'] as $key=>$val){
//IDや数量欄が空白であれば異常
if(!$val || !$request['quantity'][$key]){continue;}
//ID毎にリクエストを纏める
$product=array();
$product['id'] = $val;
$product['quantity']= $request['quantity'][$key];
$product['cat1'] = isset($request['cat1'][$key])
? $request['cat1'][$key] : 0;
$product['cat2'] = isset($request['cat2'][$key])
? $request['cat2'][$key] : 0;
//引数に異常値があれば買い物籠に投入不可
$product_check=$this->checkProductError($product);
if(!$product_check)){
$this->error_flg=true;
return false;
}
//籠に入れる用リスト
$ret[$key]=$product;
}
//返却
return $ret;
}
//---------------------------------------------------
// 配列に入ってきた商品を買い物籠に突っ込む
protected function setOrder($item_array){
//引数
if(!is_array($item_array)){return false;}
//インスタンス
$objCartSess = new SC_CartSession();
//各商品でループ
foreach($item_array as $key=>$item){
//変数整理
$product_id =$item['id'];
$quantity =$item['quantity'];
$product_cat1 =$item['cat1'] ? $item['cat1'] : "0";
$product_cat2 =$item['cat2'] ? $item['cat2'] : "0";
//実行
//エラーが出ても取得できないので困る
$objCartSess->addProduct(
array($product_id, $product_cat1, $product_cat2),$quantity
);
}
//買い物かごの集計更新
$this->setOrder_updateCart();
//返却
return true;
}
/*
setOrderのサブルーチン
買い物かごの集計を更新する
これを行わないと、合計金額などが更新されなかったりする
*/
protected function setOrder_updateCart(){
//中身はLC_Page_Cart::process()のコピペ どうなってるのかはよくわからない
//インスタンス?
global $objCampaignSess;
$objView = new SC_SiteView(false);
$objCartSess = new SC_CartSession("", false);
$objSiteSess = new SC_SiteSession();
$objCampaignSess = new SC_CampaignSession();
$objSiteInfo = $objView->objSiteInfo;
$objCustomer = new SC_Customer();
$db = new SC_Helper_DB_Ex();
$arrInfo = $objSiteInfo->data;
//実行?
$db->sfTotalCart($this, $objCartSess, $arrInfo);
$db->sfTotalConfirm($this->arrData,
$this, $objCartSess, $arrInfo, $objCustomer);
//終了?
return true;
}
//---------------------------------------------------
/*
買い物籠に投入失敗時、エラーを表示して終了
*/
protected function peinrError() {
SC_Utils_Ex::sfDispSiteError(
FREE_ERROR_MSG,false,false,self::ERROR_MESSAGE);
exit();
}
//---------------------------------------------------
/*
買い物籠に投入成功時、カートにリダイレクト
*/
protected function processSuceed() {
$this->sendRedirect($this->getLocation(URL_CART_TOP));
exit();
}
//---------------------------------------------------
/*
サブルーチン
基本的にLC_Page_Products_Detail等のコピペ、のはずだったがそうでもなくなった
*/
/*
商品ID、数量、サブカテゴリ1、サブカテゴリ2から、不正値がないかチェック
ここらへんは本来SC_Helper_DBあたりに入れるべき
ていうか、SC_Helper_Product的なものを作るべき
*/
protected function checkProductError($product){
//商品IDが数値でなければ異常
if(!SC_Utils_Ex::sfIsInt($product['id'])){
return false;
}
//該当の商品が存在しなければ異常
if(!$this->objDb->sfIsRecord("dtb_products", "product_id"
, $product['id'] , "del_flg = 0 AND status = 1")
){
return false;
}
//商品を取得できるかどうか確認
$ret=$this->getProductInfo($product);
if(is_array($ret) && isset($ret['error'])){
return false;
}
//商品の在庫チェック
//商品の購入量制限チェック
//めんどいので略
//チェックを通過
return true;
}
/*
商品ID、サブカテゴリ1、サブカテゴリ2から、商品情報を取得
*/
protected function getProductInfo($product){
//SC_Helper_DB::sfGetProductsClass()が引数をlistで受け取るので順番を設定
$product_obj=array();
$product_obj[]=$product['id'];
if($product['cat1'] != 0){
$product_obj[]= $product['cat1'];
}
if($product['cat2'] != 0){
$product_obj[]= $product['cat2'];
}
//取得
$ret=$this->objDb->sfGetProductsClass($product_obj);
if(!$ret){
return false;
}
//返却
return $ret;
}
//↓クラスのおわり
}
|
products/buy.php?product_id[]=1&cat1[]=1&cat2[]=6&quantity[]=1&product_id[]=1&cat1[]=2&cat2[]=5&quantity[]=2&product_id[]=1&cat1[]=2&cat2[]=4&quantity[]=3
などというリクエストを送信すると任意の商品をさくさく登録することができます。
実はエラー制御とかわりと適当ですが気にしない。
SC_FormParamの使い方がかなり意味不明だったので自力でやってます。
他にも各ヘルパなんかの使い方がよくわかってないので無駄っぽいことをやってる気がします。
メソッド名とかも適当に過ぎますし。
ですが、元のソースもせめてこの程度には分割してほしい。
switchの中に長大なロジックとかいい加減もう見たくないぞ。
まあ、もっと言うならFactoryMethodかなんか使えって話ですが、さすがにそこまでするのはめんどい。
EC-CUBEの記事
ソースを追うのが面倒なのですが、最終的にやってることはproduct_idに商品IDを突っ込んで送信です。
送信先での動きは2種類の経路があります。
まず商品詳細ページから。フォームの宛先は自分自身。
/html/productes/detail.php
/data/class_extends/page_extends/products/LC_Page_Products_Detail_Ex.php
/data/class/pages/products/LC_Page_Products_Detail.php
/data/class/pages/products/LC_Page.php
具体的な登録ロジックはLC_Page_Products_Detail::process()のSC_CartSessionを触っている部分です。
商品一覧、検索ページからでも購入することができますが、こちらもフォームの宛先は自分自身です。
/html/productes/list.php
/data/class_extends/page_extends/products/LC_Page_Products_List_Ex.php
/data/class/pages/products/LC_Page_Products_List.php
/data/class/pages/products/LC_Page.php
購入ロジックはLC_Page_Products_List::process()のやはりSC_CartSessionの部分です。
どちらも、商品表示を行うロジック中で、引数product_idを見つけたら買い物籠に突っ込んでショッピングカートに飛ばす、ということを行っています。
普通籠に投入する専用のクラスとか作るだろ。
また、この両者、行うことは同じなのに書き方が全然違います。
なんでこんなことになってるんだよ。
見通しのいいように、ていうか既存のロジック触りたくないので、購入処理専用のロジックを作成することにします。
まず、購入ブロックを呼び出すロジックを作成します。
内容的に/html/productsディレクトリに作成するのが無難だろうので/html/products/buy.phpを作成。
中身はブロックの呼び出しと同じ作りです。
/html/products/buy.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
/*
* 商品を買い物籠に入れる専用のロジック
* 買い物籠に入れた後はカートに移動します。
*/
//必要ファイルのインクルード、セッション呼び出しなど
require_once('../require.php');
//ロジックを実行
require_once(CLASS_EX_PATH .
"page_extends/products/LC_Page_Products_Buy_Ex.php");
$objPage = new LC_Page_Products_Buy_Ex();
register_shutdown_function(array($objPage, "destroy"));
$objPage->init();
$objPage->process();
|
/data/class_extends/page_extends/products/LC_Page_Products_Buy_Ex.phpを作成。
1
2
3
4
5
6
7
|
require_once(CLASS_PATH .
"pages/frontparts/bloc/LC_Page_FrontParts_Bloc_Buy.php");
class LC_Page_Products_Buy_Ex extends LC_Page_Products_Buy {
public function init() { parent::init(); }
public function process() { parent::process();}
public function destroy() { parent::destroy();}
}
|
/data/class/page/products/LC_Page_Products_Buy.phpを作成。
ここに実際のロジックを記入していきます。
なんと驚いたことに容量制限とやらで入りきらないので続く。
ログインしても画面移動を行わず、単にログイン状態になるだけのほうが便利だと思うので変更してみます。
幸いなことにデフォルトのログインフォームにはログイン元のページを示すurlパラメータが最初からくっついているので、ログイン終了後にそこにリダイレクトさせれば解決しそうです。
ログイン先のURLは
http://eccube.localhost/login_check.php
呼ばれるファイルは、順に
/html/frontparts/login_check.php
/data/class_extends/page_extends/frontparts/LC_Page_FrontParts_LoginCheck_Ex.php
/data/class/pages/frontparts/LC_Page_FrontParts_LoginCheck.php
/data/class/pages/LC_Page.php
となっています。
具体的にログインに成功した場合の処理を行っているのはLC_Page_FrontParts_LoginCheck::process()の以下の部分です。
1
2
3
4
5
|
if($objCustomer->getCustomerDataFromEmailPass(
$arrForm['login_pass'], $arrForm['login_email'], true)) {
$this->sendRedirect($this->getLocation(URL_DIR, array(), false));
exit;
}
|
でだ、EC-CUBEのデフォルト動作を修正する場合は/data/class/以下は触らずに、そこに対応する/data/class_extends/を触れ、ということになっています。
つまりLC_Page_FrontParts_LoginCheck::process()の動作を修正するかわりにLC_Page_FrontParts_LoginCheck_Ex::process()を修正すればよいのですが、LC_Page_FrontParts_LoginCheck::process()はひとつのメソッドに動作を全部詰め込んでいるせいで一部だけの修正というのができないんだこれが。
画面表示までやらせるなよ。いくらなんでも。
parent::process()が完全に死にメソッドじゃないか。
EC-CUBEはとにかく粒度が荒い。
LC_Page_FrontParts_LoginCheck_Ex::process()にLC_Page_FrontParts_LoginCheck::process()を全部コピペし、上記の部分だけをこのように修正。
1
2
3
4
5
6
|
if($objCustomer->getCustomerDataFromEmailPass(
$arrForm['login_pass'], $arrForm['login_email'], true)) {
$this->sendRedirect($this->getLocation(
$_REQUEST['url'], array(), false));
exit;
}
|
あっさり完成しました。
が、結局全部まるごとコピペしなければならないというこの作りはいただけない。
今回はやってませんが、プログラムを作成する際は各機能毎にメソッドに分けるようにしましょう。
そうなっていれば、今回の場合もリダイレクトするメソッドだけをオーバーライドすれば済んでいたはずですし。
あと問題点として、POSTで移動した後のページからログインするとPOSTで渡したパラメータが無くなるのでエラーになったりおかしくなったりします。
メソッドを見て処理を分岐するなり、ログインブロックでPOSTパラメータを全部常時一緒に送るようにしたりといったことが必要になります。
面倒なのでやめたほうがよかったかもしれない。
EC-CUBEの記事
前回の設定を行った場合、ブックマークなどから直通やってくるとサイトトップに移動します。
これだとわかりにくいし不便なのでログインページを表示することにしましょう。
さて、ログインページは何処にあるかというと/html/mypage/login.phpです。
しかしこれ、アドレスからしてマイページ専用であり、ログイン後は勝手にマイページに遷移してしまいます。
汎用的に使えるログインページはありません。
無ければ作りましょう。
まず/html/mypage/login.php、/html/mypage/login_check.phpをそれぞれ/html/login.php、/html/login_check.phpにコピペ。
中にある
require_once("../require.php");
を、
require_once("./require.php");
に書き換えます。
次は……実はこれだけでログインページが完成します。
テンプレートのフォームがaction="./login_check.php"、ログイン後のリダイレクトが$this->getLocation("./index.php")と、どちらも相対リンクになっているので、パスを書き換えたりしなくともたまたまログイン後はサイトトップに飛ぶようになっています。
あとは前回のリダイレクト先をログインページにすれば完了です。
どうでもいいのですがログインしていない状態で/html/mypage/に行こうとすると不正アクセスとか言われます。
ログイン状態で/html/mypage/login.phpに行こうとするときちんと/html/mypage/index.phpに飛ぶのに片手落ちだろ。
EC-CUBEの記事
EC-CUBEはデフォルト設定では、非会員の人も商品を閲覧、検索、購入できるようになっています。
非会員は特定の機能を使用できないように変更してみます。
EC-CUBEのインスタンスの基底となるファイルは/data/class/pages/LC_Page.phpです。
そして、基底ファイルはそれしかありません。
対応する/data/class_extends/page_extends/LC_Page_Ex.php的なものが存在しません。
つまり、サイト全体に関わる変更を行いたい場合は直接これをいじるしかないんだよね。
また、LC_Page::process()は、継承されたクラスでは何故か呼び出されていません。
従って、LC_Page::init()で判定を行う必要があります。
/data/class/pages/LC_Page.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
function init() {
//ログインしていない場合、指定したページの閲覧を禁止します
//2回目以降の呼び出しではログインチェックをパス
static $login_checked=false;
if($login_checked===true){return true;}
$login_checked=true;
//ログインを強要するURLリスト
$force_login_page=array(
'products/' //商品ページ、検索等
,'cart/' //買い物かご
,'shopping/' //購入ページ
);
//$force_login_pageにひっかからなければ何もしない
if($this->str_match_array(
$_SERVER['PHP_SELF'],$force_login_page) === false){
return true;
}
//ログインチェックし、ログイン状態であれば何もしない
$objCustomer = new SC_Customer();
if($objCustomer->isLoginSuccess()) {
return true;
}
//ログイン状態でなければサイトトップにリダイレクト
$this->sendRedirect(SITE_URL);
}
/*
* ↑LC_Page::init()で使ってる、単なる配列対応のpreg_match()的なもの
*/
function str_match_array($haystack,$needle_array) {
if(!is_array($needle_array)){return false;}
$needle_array=array_unique($needle_array,SORT_STRING);
foreach($needle_array as $needle){
if(strpos($haystack,$needle)!==false){
return true;
}
}
return false;
}
|
以上で、商品ページおよび検索機能、買い物かご、購入ページはログイン状態でないと使用できないようになりまし……管理画面の商品管理(/html/admin/products/)で誤爆する!
URLにadminが入ってたらログイン強要URLから排除、みたいなロジックを入れておきましょう。
会員登録やマイページ、各ブロックや前回作ったヴィジェットなどは今までどおり使用できます。
今回はサイトトップにリダイレクトしてますが、もちろんエラーページの表示などを行うこともできます。
作成しながら気になったのですが、このLC_Page、ページやブロックを呼び出すたびに毎回実行されます。
サイトトップにログインブロック、商品検索ブロック、新着情報ブロック、お薦め商品ブロック、カレンダーブロックを表示していたらLC_Page::init()がなんと6回も実行されることに。
ログイン状態は一回チェックしたら2回目以降はチェックする必要ありません。
毎回SC_Customerのインスタンス化とか非常に無駄ですので、2回目以降はパスするようにしましょう。
今回は手っ取り早くstaticで実装しています。
ブロックは/data/class/pages/frontparts/bloc/LC_Page_FrontParts_Bloc.phpのLC_Page_FrontParts_Blocを継承しているので、ブロックにだけ行いたい処理があった場合はそちらに書けばいいのですが、レイアウトだけが継承しているクラスは無く、いきなりLC_Pageを継承しています。
なのでレイアウトにだけ行いたい処理を書いたりする場所がありません。
LC_Page_FrontParts_Bloc::init()にparent::init_block()とか書いて無理矢理分類するしかないんじゃろうか。
マスタデータを覗いていたらmtb_disable_logoutなるテーブルを見つけたので、これを利用すればいちいち実装しなくてもどうにかなるのかもしれないがよくわからなかった。
↓参考
http://xoops.ec-cube.net/modules/newbb/viewtopic.php?topic_id=870&forum=10
EC-CUBEの記事
ログインブロックやカレンダーブロックといったブロック機能は、/html/frontparts/bloc/login.phpとDocumentRoot配下にあるのに、単独で利用することはできません。
直接アクセスするとエラーになります。
ちなみに、公式で紹介されているサイトでも直接見てみるとよくFatal error: require_once()とか出てくるので試してみると楽しいかもしれません。
このようなブロックをヴィジェットのようにEC-CUBEの外から使用してみます。
とりあえずヴィジェットとして扱うために/html/widget/ディレクトリを作成します。
DocumentRootの中に入れる必要は全く無いし普通は外に出すべきなんですが、どうせ/html/frontparts/とかも中にあるしどうでもいいや。
次にEC-CUBEの管轄ではない適当なファイルを作成。
/html/example.php
1
2
3
4
5
6
7
8
9
|
<?php require_once('./require.php'); ?>
<html><body>
↓ログインヴィジェット<br />
<?php require_once('widget/widget_login.php'); ?>
↑ログインヴィジェット<br />
↓商品検索ヴィジェット<br />
<?php require_once('widget/widget_search.php'); ?>
↑商品検索ヴィジェット<br />
</body></html>
|
/html/require.phpは定数設定やインクルードの他にセッションの設定なんかも行っているので、一切の出力を行う前に呼ぶ必要があります。
次にexample.phpで呼び出しているwidget_login.phpやwidget_search.phpなんかを作成。
/html/widget/widget_login.php
1
2
3
4
5
6
7
8
9
10
|
/*
EC-CUBE外からブロックをヴィジェット的に使用
ログインブロックを表示
*/
//ログインブロックにはsite.jsが必要
print('<script type="text/javascript" src="'.TPL_DIR.'js/site.js">
</script>');
//ブロック表示
require_once($require_php_dir.'/frontparts/bloc/login.php');
|
/html/widget/widget_search.php
1
2
3
4
5
6
7
|
/*
EC-CUBE外からブロックをヴィジェット的に使用
商品検索ブロックを表示
*/
//商品検索表示
require_once($require_php_dir.'/frontparts/bloc/search_products.php');
|
別ファイルにする意味がほとんど無いような中身ですが、ディレクトリ構成を見ればヴィジェットだと言うことが一目でわかりやすいということで。
とりあえず以上でEC-CUBE外からEC-CUBEのブロックをヴィジェットとして扱うことができるようになりました。
あんまり検証していないのでどんなバグが眠っているかわからんけどな。
あとインクルードした時点で画面出力まで行われてしまう作りは筋が悪いと言わざるをえない。
一応ここを参考にしたつもりだったんだけど原形を留めていない。
結局/html/require.phpと/frontparts/bloc/***.phpをインクルードすればよいという結論になりました。
EC-CUBEの記事
http://www.studio-bloom.net/archives/853
{include_php}のassign指定で変数に出力を突っ込み、その変数をSmartyの中で文字コード変換してUTF-8に揃えちゃう。これでテンプレートの中身がUTF-8に統一されるので後はウマー。
ちなみにSmartyのプラグインを作らないといけないので、
実はなにげに修正子にはPHPの全関数が使用できます(ただし$smarty->security等で禁止されている場合を除く)。
ですので、特にプラグインを作らずとも、
{$output|mb_convert_encoding:"UTF-8":"EUC-JP"}
で同じことが可能です。
……ってこれよく見たら去年の1月の記事じゃないか!
前回の記事を参考に、現在マイページに固定で書かれており流用ができない購入履歴をブロックに切り出してみます。
まず管理画面からデザイン管理→新規ブロックを作成。
ファイル名はorder_history。
中身はとりあえず空白でかまいません。
データベースにブロックが登録され、
/htdocs/user_data/packages/default/bloc/order_history.tpl
にテンプレートファイルが作成されます。
データベースを編集。
dtb_bloc.php_pathにfrontparts/bloc/order_history.phpと記入します。
/html/frontparts/bloc/order_history.phpを作成。
他のファイルをコピペして、
1
2
3
|
require_once(CLASS_EX_PATH .
"page_extends/frontparts/bloc/LC_Page_FrontParts_Bloc_OrderHistory_Ex.php");
$objPage = new LC_Page_FrontParts_Bloc_OrderHistory_Ex();
|
の2行を修正するだけです。
data/class_extends/page_extends/frontparts/bloc/LC_Page_FrontParts_Bloc_OrderHistory_Ex.phpを作成。
これまた同様に他のファイルをコピペし、
1
2
3
4
|
require_once(CLASS_PATH .
"pages/frontparts/bloc/LC_Page_FrontParts_Bloc_OrderHistory.php");
class LC_Page_FrontParts_Bloc_OrderHistory_Ex
extends LC_Page_FrontParts_Bloc_OrderHistory {
|
の2行を修正するだけです。
/data/class/pages/frontparts/bloc/LC_Page_FrontParts_Bloc_OrderHistory.phpを作成。
ここはロジックを書かないといけないのでコピペでは終われません。
購入履歴を取得するロジックはマイページを作成する部分、/data/class/pages/mypage/LC_Page_Mypage.phpのLC_Page_MyPage::process()に書かれていますので、その部分をまるごとコピペします。
同じコードを複数の箇所にコピペというのは本来やってはいけない開発手法なのですが、EC-CUBE上での解決方法がわかりませんでした。
全体を表示するわけではなくブロックを表示するだけなので、全体レイアウトを定義するSC_Helper_PageLayout_Exあたりを呼ぶ必要はありません。
またブロックなのでページングまでは要らないや、マイページに飛ばせばいいかということでページング部分も削除。
/data/class/pages/frontparts/bloc/LC_Page_FrontParts_Bloc_OrderHistory.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
require_once(CLASS_PATH . "pages/frontparts/bloc/LC_Page_FrontParts_Bloc.php");
class LC_Page_FrontParts_Bloc_OrderHistory extends LC_Page_FrontParts_Bloc {
function init() {
parent::init();
$bloc_file = 'order_history.tpl';
$this->setTplMainpage($bloc_file);
}
function process() {
$objView = new SC_SiteView();
$objQuery = new SC_Query();
$objCustomer = new SC_Customer();
// ログインチェック
if(!$objCustomer->isLoginSuccess()) {
SC_Utils_Ex::sfDispSiteError(CUSTOMER_ERROR);
}else {
//個人情報
$this->CustomerName1 = $objCustomer->getvalue('name01');
$this->CustomerName2 = $objCustomer->getvalue('name02');
}
//SQL
$col = "order_id, create_date, payment_id, payment_total";
$from = "dtb_order";
$where = "del_flg = 0 AND customer_id=?";
$arrval = array($objCustomer->getvalue('customer_id'));
$order = "order_id DESC";
//全件数
$linemax = $objQuery->count($from, $where, $arrval);
$this->tpl_linemax = $linemax;
// 取得範囲の指定(開始行番号、行数のセット)
$objQuery->setlimitoffset(SEARCH_PMAX, 0);
// 表示順序
$objQuery->setorder($order);
//購入履歴の取得
$this->arrOrder = $objQuery->select($col, $from, $where, $arrval);
// 支払い方法の取得
$objDb = new SC_Helper_DB_Ex();
$this->arrPayment = $objDb->sfGetIDValueList(
"dtb_payment", "payment_id", "payment_method");
//テンプレ
$objView->assignobj($this);
$objView->display($this->tpl_mainpage);
}
function destroy() { parent::destroy(); }}
|
生SQLとかモデルに隠せよ……
最後はテンプレートを編集します。
/htdocs/members/user_data/packages/default/bloc/order_history.tplを直接、もしくは管理画面のブロック管理からでも編集できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
<div id="order_history">
<!--{if $tpl_linemax > 0}-->
<form name="form1" method="post"
action="<!--{$smarty.const.URL_DIR}-->mypage/index.php">
<input type="hidden" name="order_id" value="" />
<p>
<!--{$CustomerName1|escape}--> <!--{$CustomerName2|escape}-->
様のご注文履歴。
</p>
<p>
最新<!--{$smarty.const.SEARCH_PMAX|escape}-->件を表示しています。
<a href="<!--{$smarty.const.URL_DIR}-->mypage/">
全ての履歴を確認</a>
</p>
<table summary="購入履歴">
<tr>
<th>購入日時</th>
<th>注文番号</th>
<th>お支払い方法</th>
<th>合計金額</th>
<th>詳細</th>
</tr>
<!--{section name=cnt loop=$arrOrder}-->
<tr>
<td><!--{$arrOrder[cnt].create_date|sfDispDBDate}--></td>
<td><!--{$arrOrder[cnt].order_id}--></td>
<!--{assign var=payment_id value="`$arrOrder[cnt].payment_id`"}-->
<td><!--{$arrPayment[$payment_id]|escape}--></td>
<td class="pricetd">
<!--{$arrOrder[cnt].payment_total|number_format}-->円
</td>
<td class="centertd">
<a href="<!--{$smarty.server.PHP_SELF|escape}-->"
onclick="fnChangeAction('
<!--{$smarty.const.URL_DIR}-->mypage/history.php');
fnKeySubmit('order_id','<!--{$arrOrder[cnt].order_id}-->');
return false">詳細</a>
</td>
</tr>
<!--{/section}-->
</table>
</form>
<!--{/if}-->
</div>
|
ここもマイページの購入履歴表示部分をそっくりコピペ。
submitのないフォームになんの意味があるかというと、詳細リンク押下を探知して送信内容を書き換えてPOST送信するというかなり意味不明なことを行っています。
素直に<a href="<!--{$smarty.const.URL_DIR}-->mypage/history.php?order_id=<!--{$arrOrder[cnt].order_id}-->">ってやって$_REQUESTで受け取れば平穏無事に穏便に終わるのに何故POSTでしか受け取れないような作りになってるんだろう。
注文番号は連番なのでリファラ隠したところで全く意味ないですし。
以上で、好きなページに購入履歴ブロックを追加することができるようになりました。
めでたし。
EC-CUBEの記事
最初から用意されているブロック要素としてログインブロックがあります。
ログアウト状態であればログインボックスを表示し、ログイン状態であれば氏名とログアウトボタンを表示してくれる優れものです。
さて、このログインボックス内では氏名を
<!--{$tpl_name1|escape}--> <!--{$tpl_name2|escape}-->
という形で取得しています。
なるほど、これを使えば氏名を取得できるんだな、と新しく作ったブロックに<!--{$tpl_name1|escape}--> <!--{$tpl_name2|escape}-->と書いても全然氏名を表示してくれません。
なんでだよ。
実は最初から用意されているブロックは、内部的にそれぞれのブロック単位で独自のPHPを実行しています。
dtb_blocテーブルにそこらへんが書かれていますが、例えばログインブロックは/html/frontparts/bloc/login.phpを呼び出して$tpl_name1や$transactionidといった値を取得しています。
ログインブロックの氏名を取得する部分だけ他のブロックから利用できないかと思えば、cookieを取得して氏名を取得してテンプレを表示するまで全部をひとつのメソッドで実行したりしているので流用できません。
せっかくdtb_bloc.tpl_pathにテンプレート名が書いてあるのに何気にそれも使っていません。なにそれ。
複数のブロックでログイン状態の表示を行いたい場合、それぞれのブロックで毎回ログイン状態を取得しないといけません。
もしかしたらどこかにコンポーネントやビヘイビア的なものがあるのかもしれませんがよくわかりませんでした。
ヘルパーはあるのですがいまいち使い方がわかりません。
というわけで特殊な情報が欲しい場合は以下のように面倒なことを行う必要があります。
とりあえず最初からあるログインブロックと同じようなものを作成してみましょう。
まず管理画面から適当にブロックを作成。
名前はなんでもいいですが、とりあえずuser_nameにしました。
中身を
姓:<!--{$tpl_name1|escape}--><br />
名:<!--{$tpl_name2|escape}--><br />
とします。
サイトトップなり適当なページなりかっらブロックを呼び出すように設定します。
初期状態では単にそのブロックが表示されるだけで、ロジックの記述は何処にもできません。
dtb_blocテーブルに今作成したブロックが記録されます。
php_pathカラムに、ブロックを呼び出したときに実行されるPHPのパスを記入。
今回は周りと揃えて
frontparts/bloc/user_name.php
としました。
/htdocs/members/frontparts/bloc/user_name.phpを作成。
中身はlogin.php等をコピペしてクラス名だけ変更します。
1
2
3
4
5
6
7
|
require_once(CLASS_EX_PATH .
"page_extends/frontparts/bloc/LC_Page_FrontParts_Bloc_UserName_Ex.php");
$objPage = new LC_Page_FrontParts_BLoc_UserName_Ex();
register_shutdown_function(array($objPage, "destroy"));
$objPage->init();
$objPage->process();
|
てか、なんでこれDocumentRoot内にあるんですかね?
ブログパーツ的にAJAXで使えるわけでもないですし意味わからん。
/data/class_extends/page_extends/frontparts/bloc/LC_Page_FrontParts_Bloc_UserName_Ex.phpを作成。
こちらもLC_Page_FrontParts_Bloc_Login_Ex.phpをコピペするだけです。
1
2
3
4
5
6
7
8
9
|
require_once(CLASS_PATH .
"pages/frontparts/bloc/LC_Page_FrontParts_Bloc_UserName.php");
class LC_Page_FrontParts_Bloc_UserName_Ex
extends LC_Page_FrontParts_Bloc_UserName {
function init() { parent::init(); }
function process() { parent::process(); }
function destroy() { parent::destroy(); }
}
|
/data/class/pages/frontparts/bloc/LC_Page_FrontParts_Bloc_UserName.phpを作成。
こちらはロジックを書く必要があります。
とりあえずはLC_Page_FrontParts_Bloc_Login.phpをコピペしてクラス名、テンプレート名を変えるだけで動作確認はできます。
以上の設定を行い、ログインしてみると、作成したブロックに氏名が表示されるようになります。
めでたし。
めんどくさいですね。
どうにかならんものじゃろうか。
たとえばカレンダーブロックに氏名を表示させたくなっただけでも、
LC_Page_FrontParts_Bloc_Calendar_Ex.phpにLC_Page_FrontParts_Bloc_Login_Exのコードをまるまるコピペさせられる羽目になります。
設計がおかしすぎる。
EC-CUBEの記事
周囲のテンプレは利用して、単に中のテキストだけ違うという、ほとんど静的な内容のページを大量に作りたいと思っても素のEC-CUBEではできません。
一ページごとにデザイン管理→新規ページ作成を行って中身を書いていくとかしないといけません。
もしかしたら方法があるかもしれませんがとりあえず見あたりませんでした。
そんなのやってらんないのでスタティックな内容は一つに纏めます。
まず上記デザイン管理→新規ページ作成を行って基準となるページを一枚作ります。
名称は変更する予定なのでデフォルト的なものを、URLは簡単にstatic.phpとかに、本文は空白(あるいは「お探しの内容は見つかりませんでした」的内容)にしておきます。
保存すると自動的にファイルが作成され、
/html/user_data/static.phpにロジックが、
/html/user_data/packages/default/static.tplにテンプレートができあがります。
ちょっとこの位置では扱いづらいのでどうにかします。
/data/Smarty/templates/default/static/フォルダを作成し、中にとりあえず適当にhoge.tplとかを入れておきます。
次に割り当てられるテンプレを変更できるようにしたいわけですが、テンプレの割り当てを行っているのはLC_Page_User::process()内の$objLayout->sfGetPageLayout($this);という部分です。
具体的なロジックはSC_Helper_PageLayout::sfGetPageLayout()に書かれているのですが、このメソッド、ファイル名から自動的にテンプレート名を算出するようになっています。
全ロジックをひとつのメソッドに突っ込んでいるうえに引数から任意のテンプレートファイルを指定する方法がありません。
つまり、テンプレを変更する手段がありません。
平たく言うとこのソースは酷い。
ちょっと穏便にどうにかする方法が見あたらなかったので、場当たり的な解決を行いたいと思います。
/html/user_data/static.phpに以下の一行を追加。
define('IS_STATIC_PAGE',true);
ついでに最初のrequire_once()が何故かフルパスにされてしまっているので、意味もなく
require_once("../require.php");
に変更しておきましょう。
/data/class_extends/helper_extends/SC_Helper_PageLayout_Ex.phpにおいて、SC_Helper_PageLayout::sfGetPageLayout()をオーバーライド。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
/*
* SC_Helper_PageLayout::sfGetPageLayout()をオーバーライド
* ひとつのURLで任意のテンプレートを選択できるようにする
*/
function sfGetPageLayout(&$objPage, $preview = false, $url = ""){
//IS_STATIC_PAGEが定義されていない場合は今までと同じ
parent::sfGetPageLayout(&$objPage, $preview, $url);
if(!defined('IS_STATIC_PAGE')){return true;}
//引数からテンプレートへのパスを作成
if(!$_REQUEST['page']){return true;}
$template_filename=TEMPLATE_DIR . 'static'
. DIRECTORY_SEPARATOR . basename($_REQUEST['page']) . '.tpl';
//テンプレートが見つかれば変更、無ければそのまま
if(is_file($template_filename)){
$objPage->tpl_mainpage=$template_filename;
}
return true;
}
|
return true;は成功したという意思表示をしているだけで、実際はreturn;だけでかまいません。
一番下はreturn;すら不要ですが、明白に書いておかないと気持ち悪いので私はいつも付けています。
http://eccube.localhost/user_data/static.php?page=hoge
というURLがやってきた場合に
/data/Smarty/templates/default/static/hoge.tplを探し、見つからなければ/html/user_data/packages/default/static.tplを表示するようになります。
めでたし。
……ひとつ重大な問題があって、
デザイン管理→ページ詳細設定で編集を行うたびに/html/user_data/static.phpが上書き保存されてしまいます。
ヒィ
どうすれバインダー。
SC_Helper_PageLayout::sfGetPageLayout()を再度書き換えます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
function sfGetPageLayout(&$objPage, $preview = false, $url = ""){
//対象のファイル以外は今までと同じ
parent::sfGetPageLayout(&$objPage, $preview, $url);
if($_SERVER['PHP_SELF']!=='/user_data/static.php'){return true;}
//引数からテンプレートへのパスを作成
if(!$_REQUEST['page']){return true;}
$template_filename=TEMPLATE_DIR . 'static' .
DIRECTORY_SEPARATOR .basename($_REQUEST['page']) . '.tpl';
//テンプレートが無ければそのまま、見つかれば変更
if(is_file($template_filename)){
$objPage->tpl_mainpage=$template_filename;
}
return true;
}
|
呼ばれたファイルがstatic.phpであればテンプレートの見直しを行い、それ以外は今まで通りとなります。
更に場当たり的な対処になりました。
新規ページ作成で作成したページはdtb_pagelayoutに入っています。
dtb_pagelayout.edit_flgを変更すれば削除はできなくなりますが、編集不可にはできないようです。
削除されると困る場合は値をとりあえず2とかにしておきましょう。
以上で、レイアウトを共有しつつ、テンプレートを追加するだけで簡単に静的ページの追加を行えるようになりました。
URLが美しくないとかいうならば
RewriteRule ^/user_data/static/(.*)$ /user_data/static.php?page=$1
とか書けばいいですが、この場合$_SERVERも変わるのでそこらへん手直しが必要です。
EC-CUBEの記事