システム奮闘記:その65

EC-CUBEでネット販売システム構築



Tweet

(2008年1月20日に掲載)
はじめに

  (注意書き)

  この奮闘記で出てきますEC-CUBEのソースや仕様は、EC-CUBE-1.3.4です。
  他のバージョンでは画面やソースの中身が異ったりする事があります。

ネット販売の刷新

2001年に開始したネット販売システム。 2004年には改良版として、写真を載せたり、商品の説明を載せたりした。 だが、最初のネット販売システムは、C言語を使ったCGIのプログラムだったが、 ムチャクチャなプログラムで、バッファオーバーフローがあったり、 ファイルロックはできていない等、「動けば良い」の発想で作った、 メチャクチャな代物だった。 そして、第2段は、PHP3言語を使った。 ユーザー認証の部分で、PHPlibを活用するため、PHP3を使った。 だが、PHP4が主流になっていた上、ネット販売が完成したすぐ後には PHP3自体、サポート切れてしまった。 それに、PHPLibを使ったセッション管理ではセッションハイジャックの問題や 私が作った部分では、SQLインジェクトなどの危険性がある。 爆弾のような存在でもあった。 2007年3月、私は、ネット販売システム利用頻度も芳しくない事から、 「撤退するのが望ましい」と提案した。 だが、役員会の結果・・・ 拡大の方向に決定! となった (^^;; さぁ、困った。 今、使っているネット販売システムは、セキュリティー的に問題なので さっさと撤去したい。 例え、セキュリティー上の問題がなかったとしても、とりあえず動けば良いで 作成したプログラムなので、ソースコードが、汚くて読めないため、 改造する気も失せる。これを自業自得という (--;; オープンソース版のネット販売のパッケージ osCoomerce がある。 だが、このソフトのソースを眺めると、PHPやHTMLのタグが 入り組んでいる上、プログラム自体が3万行ぐらいある。 事務員の私には読めませーん (^^) なのだ。 実際、6000行くらいのPukiwikiのコードを読む時ですら 発狂しそうになったのだから、3万行となると想像を絶する。 Pukiwikiについては「システム奮闘記:その52」をご覧ください  (PukiwikiでCMSサイトを構築) そこで楽天のショッピングモールや、レンタルサーバーのオプションにある ネット販売のサービスを考えたり、うちの会社の仕様に合わせた場合の osCommerceの改造費を、外注業者に依頼して見積もった。 その費用をまとめて部長に提出。そして役員会の結果・・・ 高い! 自社開発をするように! となった (--;; さぁ、困った。 適当に作るのなら自力でプログラムを作成して、ネット販売システムは 構築はできても、安全性や安定性、運用面などを考えると、 とても自力で作る力はない。頭を抱えてしまった。

オープンソースのECサイトのソフトを探す

さて、自力では構築不可能で、しかも費用をかけるなという制約があるため、 オープンソースのパッケージソフトを使う事を考えるしかない。 私が知っているオープンソース版のネット販売システムのパッケージは osCommerceというソフトだった。 しかし、3万行のソースコードと、PHPやHTMLが入り組んだコードは 発狂しそうになる。 なにせ、私は、6000行くらいのPukiwikiのコードを読む時ですら 発狂しそうになったのだから、3万行となると想像を絶する。 職業プログラマーではなく事務員なので 事務員に読めるわけねぇーよ! という感じだった。 しかも、データベースはMySQL。MySQLは触った事すらないため わかるわけねぇーよ! という状態だった。 おぞましいソース読みと、MySQLの学習を考えると、先行きが見えない状態で 暗澹たる気持ちになった。 別のソフトを探してみる。すると、ZenCartというソフトがあった。 ZenCartの日本語公式サイト(http://zen-cart.jp/) だが、osCommerceと同様、データベースはMySQLだった。 その瞬間、振出に戻った (--;;

オープンソースのEC-CUBEと出会う

お先真っ暗な気持ちの中「私が使えそうなソフトがあらへんなぁ」と 諦めかけた時、明るい光が差し込まれた。 EC-CUBEだった! しかも、大阪の企業が出しているオープンソースだった! 社名は「ロックオン」という会社だった。 (URL) http://www.lockon.co.jp/ 「ロックオンという社名はどこかで見た事があるなぁ」と思い、 名刺入れを見ていくと、ロックオンの社長の岩田さんの名刺があった。 思い出した! 6年くらい前に神戸で行われた関デジの勉強会&懇親会で名刺交換をした。 当時、岩田さんが学生さんで、学生ベンチャーとしてロックオン社を 立ち上げたのだ。元気一杯の学生さんだったのを思い出した。 夢を現実化して、なお夢を膨らませている岩田さんと、 エロ話、バカ話大好きで、「小野真弓ラブラブ」と現実逃避している 全く進歩のない私とは対象的だ (^^;; EC-CUBEは私でも使えそうだと思った。 その理由は2つある。
私にとってEC-CUBEが良いと思った理由
(1) MySQLだけでなく、PostgreSQLが使える
(2) Smartyを使っているため、プログラムと画面が分離されている。
プログラムやデザイン変更時に、PHPとHTMLが入り組んだコードを
読む必要がなくなるので、プログラムや画面の改造が楽になる

  データベース。PostgreSQLをバリバリ使えるのかと言われると
全くそんな事はない。SQL文の操作と簡単なコマンドを知っている程度だ。
  ただ、MySQLは全く触った事がないが、PostgreSQLだと導入しているだけに
その分、敷居が低く感じる。

  Smartyだが、実は2006年の夏に導入済みなのだ。
  システム奮闘記に載せていないのだが、その訳は・・・

  全く編集が追いつかないから、その部分を突っ込まないで (^^;;

  サラリーマンを辞めて、フリーライターになれば時間がとれるのだが、
フリーライターで仕事が取れる自信は全くない。キッパリ (^^)
  その上、今の会社を辞めると、ネタがなくなるので、執筆ができなくなる。


EC-CUBE導入に必要な知識

まずはEC-CUBEの導入に必要な学習項目を拾いあげる。
EC-CUBEを導入する前に必要な学習項目
(1) PostgreSQLのお勉強
(2) HTMLやCSSのお勉強
(3) クッキー、セッション管理のお勉強
(4) SSL通信のお勉強

  (1)、(2)は表面的な知識は持っている。
  (3)、(4)は、特に学習しなくても通常は支障はない。
  (5)は、通信の暗号化の話以外、全く知らない。

  EC-CUBEのインストールそのものは難しくない。
  ちなみに、インストールに関しては後述しています。

  データベースは表面的な知識さえあれば、簡単にインストールはできる。
  SSLの知識がなくても導入しなければ設定上は問題ない。
  (通信の安全性やサイトの信頼性という意味では問題があるかもしれないが)

  そのため・・・

  これなら手っ取り早く導入できるかも (^^)

  と思った。

  だが、トラブルが起こった場合、原因究明や迅速な対応を考えると
表面的な知識や、本の丸写し程度の知識では太刀打できない事は、
痛いほど身に染みている。

  そこで、できる限り、知らない事を洗い出し、潰していく作業を行う
必要があると考えた。

  だが、これらに手をつける事は・・・

  放置したままのパンドラの箱を開ける事になる!

  下手をしたら、泥沼にハマって抜けられなくなる危険性がある。
  例え、泥沼から抜けられても、泥沼との格闘や泥沼から抜け出すまでに、
相当、時間と労力を費す事が予想できる。
  そのため・・・

  全く先行きが見えへん (--;;

  そこで、上層部に対して、必要な学習項目を挙げ、いつ完成できるのか
全く見通しが立たない事を報告した。

  上層部は、渋々、認めてくれた。
  そして、パンドラの箱に手を突っ込む事にした。

PostgreSQLの学習

さて、まずはデータベースから取り掛かる。 今まで触った事のあるPostgreSQLのお勉強だ。 だが、PostgreSQLを導入して6年経つのだが・・・ 知らない事だらけなのらー (TT) SQL文を使う時、トランザクションの開始・終了の宣言をせずに、 そのまま平気でUPDATEやINSERT文を、PostgreSQLに送り込んだり、 同時接続があるにも関わらず、ロックをしていないなど、 データベースの知識がないのが、丸わかりという状態だった。 もちろん、今までに作ったプログラムを見ると、トランザクションや ロックなんぞしていない。 いくら接続数が少ないとはいえ、確率的には、同時接続の問題がある上、 2つ、3つのSQLを連続して使う際、途中で、プログラムがコケたら、 中途半端にデータが書き変わり、データの整合性に問題がある。 そのため、プログラムの書き換え作業を敢行した。これも自業自得 (--;;  詳しくは「システム奮闘記:その60」をご覧下さい。  (PostgreSQLのトランザクション、ロック、MVCC、VACUUM) そして、PostgreSQLの勉強を進めていくと、障害復旧とバックアップに 出会う。WALの話が全くわかっていなかった。 これに関しては、ハードディスクの2重化と、稼働中にソフトの不具合で コケてたとしても、WAL機能でコケた時点から開始できる事から、 何もしない事にした。 もしものために、テーブルバックアップをテープなどに保管し、 ハードディスクがオシャカになっても、WALログを守るため、 別のハードディスクに保管するのが、キチンとした方法かもしれないが、 そこまで設備がないため、現実路線を取る事にした。  詳しくは「システム奮闘記:その63」をご覧ください。  (PostgreSQLのWAL、ログ、PITR、障害時のデータ復旧の話) PostgreSQLの事を調べ出すと、知らない事が噴出する。 だが、ある程度の所で止めないと、終わりなき状態に陥る。 幸い(?)な事に、1日に何千件、何万件の接続は考えられない上、 人気歌手のコンサートチケット争奪戦のような突風が吹きつける 接続が考えられない事から、性能に関するパラメータ設定や、 SQL文の解読などの話は、手をつけない事にした。

クッキー、セッション管理の勉強

次に、クッキーとセッション管理とお勉強に取りかかる。 EC-CUBEのプログラムを眺めていると、セッション管理の関数や セッション変数が使われている事がわかった。 この辺りの話も・・・ 知らない事だらけなのらー (TT) だった。 勉強していると、ブラウザがクッキーを食べる(クッキー変数を記録)事や セッション維持のためにクッキーを使っている事を知る。 そして、ネット販売のためのカートを提供サイトは、セッション管理の技術を 応用している事を知った。 そして、この勉強を通じて、ネット販売システムの第2号目の セキュリティー面の問題点が理解できた。 PHP3 + PHPLib を使ってセッション管理を行っているため、 セッションIDが利用者に丸見えなのだ。セッションハイジャックの恐れがある。
PHP3 + PHPLIBを使ったセッション管理
PHP3 + PHPLIBを使ったセッション管理だとURLにセッションIDがまる見え
PHPLIBというライブラリがセッション管理を行う。
このライブラリがセッションIDを発行するのだが、
URLに付属する形なので、セッションIDが丸見えになってしまう (^^;;

セッションIDを盗む事によって、不正なアクセスが行う手口に使えるのだ。

  こんな事を知ると「さっさと今のネット販売システムを撤去したい」と
強く思いつつ、刷新しない限り、撤去が認められない。
  苦しい道程が続く・・・。

 詳しくは「システム奮闘記:その62」をご覧ください。
 (クッキー、PHPのセッション管理の設定)

HTMLとCSSの勉強

Webの仕組みの勉強に取り掛かる。 本を読むと、HTML言語やCSS言語について書いてあった。 システム奮闘記を編集する時、手打ちでHTMLを記述しているため、 HTML言語は知っていると思い込んでいたが・・・ 知らない事だらけなのらー (TT) だった。 HTMLはホームページのデザインを記述する言語だと思っていたのだが、 もっと奥が深い事を知る。 そもそもHTML言語は、文章の体裁を表すための言語であって、 割付け(レイアウト)を指定する言語ではないのだ。 割付けは、CSS言語で行うのだ。 文書の体裁という事から、SEO対策においてHTMLの記述が重要になる事を知る。  詳しくは「システム奮闘記:その61」をご覧ください。  (HTML、CSS言語。ちょっとXML、XHTML) だが、HTTPプロトコル、Apacheの話になると、泥沼にハマる事が予想できた。 HTTPプロトコルに着手するため、RFCをダウンロードするが・・・ 英文なんて読む気が失せる (--;; 手をつけたが最後、全く先行きが見えなくなるため、 HTTPプロトコルやApacheについては先送り(断念)する事にした。
どんどんパンドラの箱を開けていく私。 正直、しんどい。表面的な設定方法や本の丸写しの設定で済ませたい。 だが、そんな安易な道に進むわけにいかない。 なぜなら、本の丸写しで済ますと・・・ いつもドツボにハマる運命なのらー (TT) 私の場合、本の丸写しの設定だけで済ますと、不思議なくらい ドツボにハマる運命に陥る。 うれしくない事に、地雷を踏む事に関しては天才的なのだ。 こんな才能なんて、欲しくない! そのため、技術者で、表面的な知識で支障なく仕事を処理している人がいれば 「うらやましい。なんで、事務員の私が踏み込んで調べる必要があるの」と ボヤきたくなる事がある。
地雷を踏む才能なんて、欲しくない(TT)
読者の方から「技術者よりも調べている」と言われたりする。
元々、理系出身の私なので、踏み込んで調べるのは面白いと感じるが
でも、正直、しんどい。

表面的な知識だけで処理すれば楽だし、それで済むのなら、
どんどん導入事例を積み上げる事ができる。

しかし、私の場合、「地雷を踏む」という天性の才能のため、
本の丸写しだけだと、必ずドツボにハマる。
そしてトラブルに泣かされる。滅多に出ない物にも出くわす事もある。

システムの利用頻度が増すにつれ、信頼性や安定性が求められる。
業者に火消を依頼できない私にとっては、ドツボにハマる事だけは
避けたい気持ちで、踏み込んで調べているのが現状だったりする (^^;;

  パンドラの箱を開ける作業で悲鳴を挙げている間にも、月日が経っていく。
  EC-CUBEのバージョンも、私が見つけた時は「1.3.2」だったのが
「1.3.4」になっている。

  早く導入したい気持ちと、ドツボにハマる運命だけは避けたいという
葛藤の中、後者の方を押し進めていった。

EC-CUBEのインストールの方法

9月になり、データベース周辺やセッション管理辺りが おおよそ理解できてきたので、同時並行でEC-CUBEの使い方や、 うちの会社の仕様に合わせるための改造にとりかかる事にした。 まずは、EC-CUBEのインストールから。 EC-CUBEのソースを展開する。 展開した後、何も手を加えずに、最初の画面を出すのだが・・・ いきなりエラーなのらー!!
エラー画面
青く囲んだ部分を拡大してみると
/usr/local/apache/web/eccube/html/install/tempにユーザ書込み権限(777)を付与して下さい。
(注意点)

EC-CUBEを展開する時のユーザーや、ユーザーの環境設定により
このエラーがでない場合があります。
ユーザーの環境設定状態で、展開時のパーミッションに影響が出るだけで
心配するエラーではありません。

  なので、パーミッションを変更する。

パーミッションの変更>
[root@server]# chmod 777 /usr/local/apache/web/eccube/html/install/temp
[root@server]#

  パーミッションを変更して、気を取り直してブラウザの「更新」ボタンを押す。
  すると以下の画面が出てくる。

インストールの画面
EC-CUBEのインストール開始画面
もちろん「次へ進む」を選ぶ。

  次に進めると、以下のエラー画面が出てくる。

エラー画面
EC-CUBEのインストール開始時にエラー
青く囲んだ部分を拡大してみると
>> ×:/usr/local/apache/web/eccube/data/install.php(644)
ユーザ書込み権限(666)を付与して下さい。
>> ×:/usr/local/apache/web/eccube/html/user_data(755)
ユーザ書込み権限(777)を付与して下さい。
>> ×:/usr/local/apache/web/eccube/html/cp(755)
ユーザ書込み権限(777)を付与して下さい。
>> ×:/usr/local/apache/web/eccube/html/upload(755)
ユーザ書込み権限(777)を付与して下さい。
>> ×:/usr/local/apache/web/eccube/data/Smarty/templates_c(755)
ユーザ書込み権限(777)を付与して下さい。
>> ×:/usr/local/apache/web/eccube/data/downloads(755)
ユーザ書込み権限(777)を付与して下さい。
>> ×:/usr/local/apache/web/eccube/data/logs(755)
ユーザ書込み権限(777)を付与して下さい。
(注意点)

EC-CUBEを展開する時のユーザーや、ユーザーの環境設定により
このエラーがでない場合があります。
ユーザーの環境設定状態で、展開時のパーミッションに影響が出るだけで
心配するエラーではありません。

  気にするエラーではないので、パーミッションを変えて対応する。

パーミッションの変更
[root@server]# chmod 666 /usr/local/apache/web/eccube/data/install.php
[root@server]# chmod 777 /usr/local/apache/web/eccube/html/user_data
[root@server]# chmod 777 /usr/local/apache/web/eccube/html/cp
[root@server]# chmod 777 /usr/local/apache/web/eccube/html/upload
[root@server]# chmod 777 /usr/local/apache/web/eccube/data/Smarty/templates_c
[root@server]# chmod 777 /usr/local/apache/web/eccube/data/downloads
[root@server]# chmod 777 /usr/local/apache/web/eccube/data/logs

  そして、ブラウザの「更新」ボタンを押すと、以下の画面が出てくる。

パーミッションが正しい事のお知らせ
ディレクトリ権限の点検合格
青く囲んだ部分を拡大してみると
>> ○:/usr/local/apache/web/eccube/data/install.php(666)
アクセス権限は正常です。
>> ○:/usr/local/apache/web/eccube/html/user_data(777)
アクセス権限は正常です。
>> ○:/usr/local/apache/web/eccube/html/cp(777)
アクセス権限は正常です。
>> ○:/usr/local/apache/web/eccube/html/upload(777)
アクセス権限は正常です。
>> ○:/usr/local/apache/web/eccube/data/Smarty/templates_c(777)
アクセス権限は正常です。
>> ○:/usr/local/apache/web/eccube/data/downloads(777)
アクセス権限は正常です。
>> ○:/usr/local/apache/web/eccube/data/logs(777)
アクセス権限は正常です。

  「次へ進む」を選ぶと、以下の画面が出てくる。

ファイル等のコピー成功のおしらせ
ファイルのコピーの成功画面
青く囲んだ部分を拡大してみると
../user_data/css/contents.css:コピー成功
../user_data/include/bloc/best5.tpl:コピー成功
../user_data/include/bloc/cart.tpl:コピー成功
../user_data/include/bloc/category.tpl:コピー成功

(途中、省略)

../upload/save_image/08311311_44f661811fec0.jpg:コピー成功
../upload/save_image/08311313_44f661dc649fb.jpg:コピー成功
../upload/save_image/08311313_44f661e5698a6.jpg:コピー成功

  「次へ進む」を選ぶ。
  すると、店舗情報を記入する画面が出てくる。

店舗情報を記入する画面
EC-CUBEでの店舗情報入力画面

  入力部分を拡大してみると

店舗情報を記入する画面(拡大)
EC-CUBEでの店舗情報入力画面の拡大
店舗名、店舗の問い合わせのメールアドレス、
管理者のログイン名、パスワードを選ぶ。

そして、画面や各種データの入ったディレクトリーの指定だが、
これは初期値で問題ない。

URL(セキュア)は、SSLを使った場合のURLを指定なので、
SSLを使う場合はhttpsで始まるURLを入力すれば良い。
SSLを使わない場合は、URL(通常)と同じ物を入力すれば良い。

  必要事項を記入して「次へ進む」を選ぶ。
  すると以下の画面が出てくる。

データベースの選択画面
EC-CUBEで使うデータベースをPostgreSQLかMySQLかの選択画面

  入力部分を拡大してみると

データベースの選択画面
EC-CUBEで使うデータベースをPostgreSQLかMySQLかの選択画面を拡大
ここでデータベースをPostgreSQLか、MySQLにするかを選択できる。

データベースのサーバーをIPやホスト名で指定する。
必ずしもEC-CUBEを入れるサーバーと、データベースサーバーは
同じ物を使う必要がない事を意味する。

そして、データベース名、データベースのユーザー名、
パスワードを記入する。

  「次へ進む」を選ぶ。
  データベースを初期化するかどうかの選択画面が出てくる。

データベースの初期化の選択画面
データベース PostgreSQLの初期化

  画面を拡大してみると

データベースの初期化の選択画面
PostgreSQLの初期化画面の拡大
初めてのインストールの場合は、チェックを入れない(初期化する)

ただ、既に、1度インストール済みで、既に入っているデータベースの
データの中身を生かしたい場合は、チェックを入れる。(初期化を行わない)

  データベースの初期化を選び、「次へ進む」を選ぶと
データベースの初期化の結果が出力される。

データベースの初期化成功の画面
PostgreSQLの初期化成功画面

  「次へ進む」を選ぶ。
  すると、データベースとPHP言語のバージョンを
EC-CUBEの開発元に知らせるかどうかの選択が出てくる。

データベース、PHPのバージョンを
開発元に知らせるかどうかの選択
データベース、PHPのバージョンを開発元へ知らせるかどうかの有無

  拡大してみると

データベース、PHPのバージョンを
開発元に知らせるかどうかの選択
データベース、PHPのバージョンを開発元へ知らせるに印をつける
できるだけ開発に協力したいと思う方は「はい」を選び
「見ちゃいや〜ん。ひ・み・つ」という方は「いいえ」を選択します。

  「次へ進む」を選ぶと、インストール完了の画面が出てくる。

インストール完了の画面
EC-CUBEインストールの完了画面

  これで、ほぼインストール作業は完了だ。
  上の図の青い文字の「管理画面」を選ぶと、管理者専用のログイン画面が
出てくる。

管理者専用のログイン画面
あとは画面の上に表示されている指示にしたがって
不要になったインストールファイルを削除するだけで完了だ。

  インストールの方法を書きましたが、特にシステム的な専門知識がなくても
ブラウザを使って手軽に行えます。

EC-CUBEのインストールの際の注意点

EC-CUBEでは、商品の画像をアップロードして、画像を表示する事ができる。 だが、そのまま画像を載せるのではなく、EC-CUBEのプログラム内で 画像の大きさ等を加工しているのだ。 そんな事を知らなかったため、PHPのコンパイルの際、GDライブラリや、 libjpeg、libpngのオプションを外していたため、画像をアップロードする際、 以下のエラーが出た。
エラー画面
EC-CUBEのインストール中に発生したエラー
エラーの内容(拡大)
Fatal error: Call to undefined function: imagecreatefromjpeg() in
/usr/local/apache/web/eccube/data/lib/gdthumb.php on line 203
imagecreatefromjpeg()関数が定義されていないというエラーだ。
これはファイルからjpgファイルを生成する関数だ。

エラーが出るという事は、jpg関係のライブラリが
PHPと連動していない事が言える。

  PHPのコンパイルの際、画像関係のライブラリを取り込むオプションを
付けていなかったので、再コンパイルをして問題を解消した。

  EC-CUBEはアップロードした画像を適切な大きさに加工したりするため
これらのライブラリが必要になるので、注意が必要だ。


EC-CUBEの設定と管理画面

EC-CUBEの管理画面は技術的な知識がなくても、ネット販売の売上管理 顧客管理、店舗管理などができるようになっている。
管理者専用画面(トップ画面)
EC-CUBEの管理者専用画面
トップ画面を開くと、使っているEC-CUBE、データベース、PHPの
バージョンの表示が出てくる。

それだけでなく、売上の集計なども表示される。

  ブラウザを使って、ネット店舗の設定が簡単にできる。

  機能紹介をしていきたい所だが、全部紹介を書いたら

  私の方も編集が大変なのらー!!

  その上、既にEC-CUBEのサイトの方が詳細に書かれていますため、
私が二番煎じや三番煎じで紹介を書いても大した内容は書けない。
  なので、機能の一部だけを紹介します。


  消費税と送料の設定について説明します。

  管理画面の「基本情報設定」を選びますと、店舗情報の入力や
消費税と送料の設定の項目が出てきます。

消費税と送料の設定
EC-CUBEの管理者専用画面(消費税と送料の設定)
消費税率が変動しても対応できるようになっている。
無策で無駄使い大好きな日本政府が安易な消費税を上げて
国民は痛めつけられても、システム的には問題ない。

消費税計算の端数の処理も選択できる。

送料は「XXXX円以上お買い上げの人は無料」という設定ができる。
この商習慣は日本独特らしく、欧米で生まれたECサイトのソフト
ZenCartや、osCommerceにはついていないようだ。
日本語対応版には付いているらしいが・・・。
なので、さすがは「日本発! 大阪発!」と思わせるソフトだ。

  消費税の端数問題。
  消費税率が現状「5%」のため、例えば、330円の商品の場合、
消費税は単純計算すると「16.5」円になる。0.5円端数になる。

  だが、困った事に端数の扱いは税法では規定がない。
  四捨五入するか、端数切捨て、端数切り上げは個々の企業の判断に
任されている。
  なので、この機能はありがたい。

  ただ、うちの会社の消費税計算は、もう少し厄介だ。
  1伝票辺りの商品の合計金額から消費税を出して、四捨五入するため、
個々の商品の価格ごとの消費税の端数処理と比較すると、
1円、2円ぐらいの違算が発生してしまう場合がある。

  今までは、うちの会社のネット販売でも、既存のお客さんだったので
掛売り中心だった。そのため、税抜き合計を表示して、
請求書の時に消費税込むの請求をしていたのだが、
銀行振込などをできるようにしたため、伝票処理の際に、
微妙な調整が必要になった。


  でも、今更、キチンとした端数の規定なんぞ設定したら、
各企業で膨大なシステムの修正の手間が発生するため、
今となっては、現状のままの方が良いかもしれない。

  財務省、国税庁よ、キチンとした規定を最初から設けろ!
  誰の税金で飯食わしてもらっているのだ! 手間ばかり増やしやがって!


  売上管理、顧客管理、商品管理などブラウザー上で設定できる。
  でも、私が書くよりも、EC-CUBEのサイトの方が詳細でわかりやすいと
思いますので、省略します。← これを単なる編集の手抜きという (^^;;

EC-CUBEの管理画面の問題点

EC-CUBE1.3.4には、管理画面の部分で、やや問題点があった。 (1.4.5や2.0以降では解消されているかもしれませんが) EC-CUBEの利点として、管理者画面を使って、ブロックなどの画面の テンプレートの書き換えができる。
管理者専用画面(デザイン管理)
EC-CUBEの管理者専用画面(デザイン管理)
デザイン管理の部分を選ぶと、各ブロックの配置を変更する事ができる。
これによってネット販売システムの最初のページのレイアウトを
色々、簡単に変更する事ができる。

  そして、各ブロックごとのテンプレートを追加、編集する事ができる。
  そこで「ブロック編集」を選ぶ。

管理者専用画面(ブロック編集)
EC-CUBEの>管理者専用画面(ブロック編集)
テンプレートの名前と、ファイル名を指定して
テンプレートを記述する事ができる。

  追加だけでなく、既存のブロックのテンプレートの書き換えもできる。

管理者専用画面(ブロック編集)
テンプレート呼び出し
既存のブロックの中身を呼び出して、それを書き換える事ができる。
これによって、ブロックのデザインを自由に変更できる。

  もちろん、編集するのにHTMLの知識は必要な上、ブロックの部分によっては
Smartyの知識も必要にある。
  なので、HTMLを触った事のない人にとっては不便に思われるが、
知っている人にとっては

  非常に便利やん!

  となる。

  だが、この機能に問題点があった。

テンプレート編集画面
テンプレート編集画面
既存のブロックを呼び出た状態。
この時点では何も問題はない。

  さて、これを「保存」をクリックして、もう一度、呼び出してみる。
  すると以下の状態になってしまう。

テンプレート編集画面
テンプレート編集画面で発生する問題点
「"」の前に「¥」マークがついてしまう。
これによって、表示がおかしくなったりする。

  そのためテンプレートの書き換えは、直接、テンプレートが置かれている
ディレクトリまでいき、ファイルをemacsで編集する事にした。

  HTMLやSmartyの知識があれば、emacsなどで編集するのは
苦にならないので、全然、気にならない。

  それに、その他の部分では、管理者画面で色々設定できるため
非常に便利です。
  その辺りの便利さについては、実際に触ってみて体感されるのを
お勧めします♪

オープンソースだから可能。EC-CUBEの改造について

EC-CUBEだが、そのままでは、うちの会社の業務に合わない部分がる。 そこで、うちの会社の仕様に合わせるため改造する必要がある。 自由に改造できるのがオープンソースの利点なのら!! プログラマーでもデザイナーでもないので、凄い改造はしていませんが、 そこで、私が行った改造を紹介したいと思います。 だが、その前に改造を行う際の話をしたいと思います。
■ ディレクトリー構造 EC-CUBEのインストールは簡単に出来た。 でも、これで終わりではなく、ここからが出発点なのだ。 うちの会社の仕様に合わせて、改造を行う必要があるからだ。 EC-CUBEのサイトや、実際のディレクトリーを眺めながら ディレクトリー構造を把握しようと考えた。
大雑把なディレクトリー構造
EC-CUBEのディレクトリー構造
htmlディレクトリーは、PHPのプログラムが格納されている。
dataディレクトリーは、閲覧者には見られたくないデータが
入っていそうな気がする。

  dataディレクトリーには一体、どんな物が格納されているのだろうか。
  もう少し見ていくと次の通りになる。

dataディレクトリーの中身について
dataディレクトリー構造
htmlディレクトリー内で動くPHPプログラムの共通ライブラリ、
共通クラス、画面表示のためのテンプレートが入っている。

それ以外にも、dataディレクトリーには、install.phpファイルがあり
プログラムの絶対パスやデータベースの情報などを格納しています。

  プログラムを動かす際に必要なクラス、ライブラリ等は
セキュリティー上、直接、外部から見れないディレクトリーに格納されている。


  さて、PHPプログラムと画面表示のテンプレートとのディレクトリーの
対応関係を見ていく。

PHPプログラムと画面表示のテンプレートとの対応関係
>PHPプログラムと画面表示のテンプレートとの対応関係
テンプレートそのものも、Smartyで使う関数処理などを含むため
基本的には、直接、外部から見れないディレクトリーに格納するのが
常套手段だ。

  簡単に図を描いているようですが、実は、EC-CUBEのプログラムを見た時、
全体像が把握できず、試行錯誤、プログラムやディレクトリー構造を見ながら
上の図式になっている事に気づきました。


  だが、テンプレートでも、管理画面で編集可能な部分に関しては
PHPのプログラム部分とテンプレート部分との間のディレクトリーの対応関係は
次のようになっている。

PHPプログラムと画面表示のテンプレートとの対応関係
ユーザーが管理画面を使って加工ができる部分
EC-CUBEの改造を行う上での対応関係図
ユーザーが管理画面を使って加工できるテンプレートは
ブラウザが直接見れる、HTMLディレクトリーに置かれている。

そのため、PHPプログラムとテンプレートの対応関係が
上の図のようになっている。

  もちろん、プログラムやディレクトリーを眺めながら見ていったので
この事に気づくのにも時間がかかったが、把握してしまえば難しくはない。

  非常にわかりやすいディレクトリー構造と対応関係になっているからだ。


■ Smartyのテンプレートと違う点 EC-CUBEは、Smartyを使って、PHPのプログラム部分と、HTMLのテンプレートを 分離させている。そのため、見やすい上、プログラマーとデザイナーが 分担しながらEC-CUBEを触る事ができる。 だが、Smartyで使う関数や、PHPから送られる変数の表示を行う場合、 SmartyとEC-CUBEで使うSmartyのテンプレートでは違った規則がある。 最初、テンプレートを編集する際だった。 以下のテンプレートを見た。
html/user_data/include/bloc/login.tpl の一部抜粋
<!--{*
 * Copyright(c) 2000-2007 LOCKON CO.,LTD. All Rights Reserved.
 *
 * http://www.lockon.co.jp/
 *}-->
<!--▼ログインここから-->
<!--{if $smarty.post.url == ""}-->
        <!--{if sfIsHTTPS()}-->
                <!--{assign var=url value="https://`$smarty.server.HTTP_HOST``$smarty.server.REQUEST_URI`"}-->
        <!--{else}-->
                <!--{assign var=url value="http://`$smarty.server.HTTP_HOST``$smarty.server.REQUEST_URI`"}-->
        <!--{/if}-->
<!--{else}-->
        <!--{assign var=url value="`$smarty.post.url`"}-->
<!--{/if}-->

  このテンプレートを見た時、コメントが多いなぁと思った。
  なぜならHTML文の中にコメントを入れる場合は以下のようにするからだ

  <!-- コメント文  -->

  なので、コメント文が多いなぁと思った。

  でも、なんだかSmartyの関数処理の部分と、そっくりなので、
必要に応じてコメントを外す物だと思った。
  そこで以下のように書き換えてみた。

html/user_data/include/bloc/login.tpl の一部抜粋
<!--{*
 * Copyright(c) 2000-2007 LOCKON CO.,LTD. All Rights Reserved.
 *
 * http://www.lockon.co.jp/
 *}-->
<!--▼ログインここから-->
{if $smarty.post.url == ""}
        <!--{if sfIsHTTPS()}-->
                <!--{assign var=url value="https://`$smarty.server.HTTP_HOST``$smarty.server.REQUEST_URI`"}-->
        <!--{else}-->
                <!--{assign var=url value="http://`$smarty.server.HTTP_HOST``$smarty.server.REQUEST_URI`"}-->
        <!--{/if}-->
<!--{else}-->
        <!--{assign var=url value="`$smarty.post.url`"}-->
{/if}
青い部分は<!-- -->を外した物だ。
これで、どんな動きをするのか見てみる事にした。

  動かしてみると

  なんで、エラーが出んねん

  だった。

  そこで考えてみたり、プログラムを見てみて、次の事がわかってきた。
  どうやらSmartyの使い方とは少し違った設定をしている可能性がある。

関数へ変数の表示を行う上で
SmartyとEC-CUBEのSmartyの違う部分
Smarty EC-CUBE
{ 関数や変数 } <!--{ 関数や変数 }-->

  この事を知らないと、コメント文だと勘違いしてしまう。

  さて、この細工(?)の設定だが、どうやら以下のクラスで設定しているようだ。

data/class/SC_View.php の一部抜粋
class SC_View {

    var $_smarty;
        var $objSiteInfo; // サイト情報

    // コンストラクタ
    function SC_View($siteinfo = true) {
                global $SC_VIEW_PHP_DIR;

        $this->_smarty = new Smarty;
                $this->_smarty->left_delimiter = '<!--{';
                $this->_smarty->right_delimiter = '}-->';
青い部分と赤い部分が該当の部分になる。
なぜ、EC-CUBEの場合、コメント文に近い形にしたのか
意図はわからないが、何かあるのだと思う。


■ 改造その1・価格を伏せる 諸事情により、会員登録したお客さん以外は価格を伏せる事になった。 だが、EC-CUBEの初期設定では、会員用のログインなしでも 商品の価格を表示するようになっている。
商品の一覧を表示すると価格が表示される
初期時は商品価格が表示
普通なら、これで問題がないのだが、諸事情によって
ログインしないと、価格が表示されない仕掛けにしておく必要がある。

そして、ログインしないと商品が購入できないようにするため、
「カゴに入れる」の部分も表示しないようにする必要がある。

  ログインしていない状態では価格を伏せるように改造する事にした。

  さて、商品一覧を表示するのに必要なプログラムの部分を見てみる。

商品一覧を表示に必要な物
プログラム名 html/products/list.php
テーンプレート html/user_data/templates/list.tpl

  だが、困った問題があった。

  どないして、ログインの判定すれば、ええねん (--;;

  根本的な所で、つまずいた。

  以前なら、「事務員なので、わかりませーん」で終わる私だが、
ログイン画面のブロックを見ると、ログイン前とログイン後の画面に違いがある。

ログインのブロック表示
ログイン前 ログイン後
ログイン前のログインブロック画面 ログイン後のログインブロック画面
この2つを見ると、明らかにログインの有無の判定している事がわかる。

  そこで思った。

  ログインのブロック部分を見れば、ええかも (^^)

  ログインのブロックのテンプレートに、判定している部分があった。
  (html/user_data/include/bloc/login.tpl)

  早速、見てみる。

html/user_data/include/bloc/login.tpl を抜粋
<!--{if $tpl_login}-->
<tr>
<td align="center" colspan="3" class="fs12">ようこそ <br>
<!--{$tpl_name1|escape}--> <!--{$tpl_name2|escape}--> 様<br />
赤い部分が条件文になっている。
もし、ログインしている状態なら、「ようこそ」と表示される
仕掛けになっている。

  さて、html/user_data/include/bloc/login.tplのテンプレートを
表示させるために動かすプログラムを探してみる。
  html/frontparts/bloc/login.php だった。

  早速、プログラムの中身を眺めてみる。
  案の定、ログインの判定部分の記述があった。

ログイン判定の部分
(html/frontparts/bloc/login.php を抜粋)
$objCustomer = new SC_Customer();

(途中、省略)

// ログイン判定
if($objCustomer->isLoginSuccess()) {
        $objSubPage->tpl_login = true;
        $objSubPage->tpl_user_point = $objCustomer->getValue('point');
        $objSubPage->tpl_name1 = $objCustomer->getValue('name01');
        $objSubPage->tpl_name2 = $objCustomer->getValue('name02');
} else {
        // クッキー判定
        $objSubPage->tpl_login_email = $objCookie->getCookie('login_email');
        if($objSubPage->tpl_login_email != "") {
                $objSubPage->tpl_login_memory = "1";
        }

        // POSTされてきたIDがある場合は優先する。
        if($_POST['login_email'] != "") {
                $objSubPage->tpl_login_email = $_POST['login_email'];
        }
}
青い部分がログインの有無を判定する部分だ。
ここで判定させて、ログインのブロック画面の表示を変えている。

$objCustomerのオブジェクトだが、
赤い部分でオブジェクトの宣言している。
このオブジェクトは、SC_Customer()クラスを使っている。

  さて、ログイン判定の有無の部分がわかった。
  一歩踏み込んで、ログイン判定のオブジェクトを形成している
SC_Customer()クラスを見てみる必要がある。

  クラスに関するプログラムが格納しているディレクトリーを見てみる。
  すると以下のソースでクラスの定義があった。
  (data/class/SC_Customer.php)

  SC_Customer()クラスの「isLoginSuccess()」関数で判定させているので、
「isLoginSuccess()」の関数を見てみる。

SC_Customer()クラスの「isLoginSuccess()」関数
// ログインに成功しているか判定する。
function isLoginSuccess($dont_check_email_mobile = false) {
        // ログイン時のメールアドレスとDBのメールアドレスが一致している場合
        if(sfIsInt($_SESSION['customer']['customer_id'])) {
                $objQuery = new SC_Query();
                $email = $objQuery->get("dtb_customer", "email", "customer_id = ?", array($_SESSION['customer']['customer_id']));
                if($email == $_SESSION['customer']['email']) {
                        // モバイルサイトの場合は携帯のメールアドレスが登録されていることもチェックする。
                        // ただし $dont_check_email_mobile が true の場合はチェックしない。
                        if (defined('MOBILE_SITE') && !$dont_check_email_mobile) {
                                $email_mobile =
				$objQuery->get("dtb_customer",
				"email_mobile", "customer_id = ?", array($_SESSION['customer']['customer_id']));
                                return isset($email_mobile);
                        }
                        return true;
                }
        }
        return false;
}
断片的にしか見ていないので、これだけでは、よくわからん。
少なくとも、ログイン成功後、セッション変数にログイン成功の
印を保管させている事が言えると思う。

  さすがに、プログラム読みは苦手なので、ソースを丹念に追っかけていく気が
起こらないのだが、少なくともセッション変数を使って、
ログイン成功を保管させている事がわかった。

  そこで考えた。
  価格表示のプログラムに、同じようにログインの判定を入れれば良いのだ。

html/frontparts/bloc/login.php に
これを入れれば良いのだ!
⁄⁄ログインの有無の判定
$objPage->tpl_login = false ;
$objCustomer = new SC_Customer();
if($objCustomer->isLoginSuccess()) {
        $objPage->tpl_login = true;
        }
$objPage->tpl_login の変数を作る事で
テンプレートに値を送る事ができる。

  そして価格表示のテンプレートを以下のように、手を加える。

html/user_data/include/bloc/login.tpl に
手を加えれば良いのだ!
書き換え前
<td>
        <span class="red"><span class="fs12">価格</span><span class="fs10">(税込)</span></span><span class="redst"><span class="fs12">:
        <!--{if $arrProducts[cnt].price02_min == $arrProducts[cnt].price02_max}-->
                <!--{$arrProducts[cnt].price02_min|sfPreTax:$arrSiteInfo.tax:$arrSiteInfo.tax_rule|number_format}-->
        <!--{else}-->
                <!--{$arrProducts[cnt].price02_min|sfPreTax:$arrSiteInfo.tax:$arrSiteInfo.tax_rule|number_format}-->〜<!--{$arrProducts[cnt].price02_max|sfPreTax:$arrSiteInfo.tax:$arrSiteInfo.tax_rule|number_format}-->
        <!--{/if}-->
        円</span></span>
</td>
書き換え後
<td>
        <!--{if $tpl_login}-->
        <span class="red"><span class="fs12">価格</span><span class="fs10">(税込)</span></span><span class="redst"><span class="fs12">:
        <!--{if $arrProducts[cnt].price02_min == $arrProducts[cnt].price02_max}-->
                <!--{$arrProducts[cnt].price02_min|sfPreTax:$arrSiteInfo.tax:$arrSiteInfo.tax_rule|number_format}-->
        <!--{else}-->
                <!--{$arrProducts[cnt].price02_min|sfPreTax:$arrSiteInfo.tax:$arrSiteInfo.tax_rule|number_format}-->〜<!--{$arrProducts[cnt].price02_max|sfPreTax:$arrSiteInfo.tax:$arrSiteInfo.tax_rule|number_format}-->
        <!--{/if}-->
        円</span></span>
        <!--{/if}-->
</td>

  これで値段は表示されない。
  あとは、ログイン前は購入できないようにするため、「買い物カゴに入れる」を
表示できなくすれば良い。
  それも上の書き換えと同様の事をすれば良い。

  その結果、ログイン前では値段や買い物カゴの表示がなくなった。


プログラムの改造結果
プログラム改造前
改造前は価格が表示
プログラム改造後
改造後は価格が非表示
ログインする前は、価格と買い物カゴを隠す事ができた。
これによって、ログインしないと購入できなくした。

  これで、うちの会社用に改造できた (^^)


  でも、これで良いわけではない。
  トップページで買い物カゴの状態が見れてしまうのだ。

トップページの状態
EC-CUBEのトップページ
ログイン前なので買い物ができず、例え、購入金額がゼロであっても、
買い物カゴを表示させるのは、いささか不格好にも思える。
そのため、ログインする前は、買い物カゴの表示を消そうと考えた。

  そこで、プログラムを改造する。
  といっても改造するほどの物ではない。

  ログイン前は html/frontparts/bloc/cart.php の機能を止めれば良いのだ。

プログラムの改造
(html/frontparts/bloc/cart.php)
改造前
<?php
/*
 * Copyright(c) 2000-2007 LOCKON CO.,LTD. All Rights Reserved.
 *
 * http://www.lockon.co.jp/
 */
class LC_CartPage {
        function LC_CartPage() {
                /** 必ず変更する **/
                $this->tpl_mainpage = BLOC_PATH . 'cart.tpl';   // メイン
        }
}

$objSubPage = new LC_CartPage();
$objSubView = new SC_SiteView();
$objCart = new SC_CartSession();
$objSiteInfo = new SC_SiteInfo;


   (途中、省略)


$objSubView->assignobj($objSubPage);
$objSubView->display($objSubPage->tpl_mainpage);
改造後
<?php
/*
 * Copyright(c) 2000-2007 LOCKON CO.,LTD. All Rights Reserved.
 *
 * http://www.lockon.co.jp/
 */
class LC_CartPage {
        function LC_CartPage() {
                /** 必ず変更する **/
                $this->tpl_mainpage = BLOC_PATH . 'cart.tpl';   // メイン
        }
}

// ログイン判定
$objCustomer = new SC_Customer();


$objSubPage = new LC_CartPage();
$objSubView = new SC_SiteView();
$objCart = new SC_CartSession();
$objSiteInfo = new SC_SiteInfo;


   (途中、省略)

$objSubView->assignobj($objSubPage);
//  ログイン前はテンプレートの表示を止める
if($objCustomer->isLoginSuccess()) {
$objSubView->display($objSubPage->tpl_mainpage);
}
ピンクの部分は追加した部分。
ここでログイン判定のためのオブジェクトを宣言する。

赤い部分はログイン前か後かを点検し、ログイン前なら
テンプレートを表示させないようにする。

  これによって、ログイン前のトップページが以下のように変わった。

トップページの状態
ログイン前は買い物かごを隠した

  これで一応、格好がついた (^^)


■ 改造その2・規格別の値段の一覧表示 EC-CUBEでは、同じ商品名でも、規格別に値段や商品コードの設定ができる。 そのためアイスクリームでも「味」という規格別に「抹茶150円」や 「チョコレート:200円」という形にする事ができる。
規格別に値段が設定できる
規格の区分けは2種類
アイスクリームという商品でも、規格1、規格2という具合に
商品の細かい種類の分類が可能だ。
EC-CUBEの初期設定ではアイスクリームでも「味」と「大きさ」の
分類にわけている。
同一商品でも規格が異なれば値段も違う事ができる
そして、規格別に商品番号や値段の設定が可能だ。

  同じ商品を規格別に分類できる事は、大きな利点がある。
  大きさや色が違うだけで、個別に商品展示を行う必要がなくなる。
  もし、色が多い商品だと、商品展示だけでも凄い数になるからだ。

  だが、値段表示に難がある。
  以下のように、規格別で値段に違いがある場合、最安値と最高値の範囲しか
表示されないのだ。

値段の表示に難あり
規格ごとに値段が違うと表示がわかりずらい
規格別に分ける事によって、味の違い、大きさの違いの商品を
商品一覧に並べる事なく、同じ商品名から選択肢で選べる利点がある。

だが、EC-CUBEの初期設定では、値段表示が最安値と最高値しか
掲載されないのだ。

  この場合、買い物カゴに入れて、明細を確認しない限り、
購入したい物の金額がわからない問題がある。


  そこで、利用者に使いやすくするためにも、規格別の価格一覧表を
表示させる必要があると考えた。

  だが・・・

  どないすれば、ええねん (--;;

  だった。
  でも、オープンソースだからプログラムを触れば、なんとかなると思った。

  そこで、何か手がかりがないかと考え、規格の登録画面を動かす
プログラムに着目した。

  まずは商品一覧の表示プログラムを見てみる事にした。
  (html/products/list.php)

商品一覧の表示プログラム
html/products/list.php の一部を抜粋
// 検索結果の取得
$objPage->arrProducts = $objQuery->select("*", "vw_products_allclass AS allcls", $where, $arrval);

// 規格名一覧
$arrClassName = sfGetIDValueList("dtb_class", "class_id", "name");
// 規格分類名一覧
$arrClassCatName = sfGetIDValueList("dtb_classcategory", "classcategory_id", "name");
// 企画セレクトボックス設定
if($disp_num == 15) {
    for($i = 0; $i < count($objPage->arrProducts); $i++) {
        $objPage = lfMakeSelect($objPage->arrProducts[$i]['product_id'], $arrClassName, $arrClassCatName);
        // 購入制限数を取得
        $objPage = lfGetSaleLimit($objPage->arrProducts[$i]);
    }
}
検索結果の取得というコメントをみて、この辺りを見れば良いと思った。
青い部分を見て商品関連の情報の取得を行っている感じだ。

そして、赤い部分のlfMakeSelect()関数に注目した。

ちなみに、この関数に着目すれば良いと思ったのは、「なんとなく」だったが
結果的に正しい事がわかった。

  そこで、lfMakeSelect()関数を定義している部分を見てみる事にした。

  すると、これぞと思う部分に出くわした。

商品一覧の表示プログラム
lfMakeSelect()関数の定義部分の一部
(html/products/list.php)

// 商品規格情報の取得    
$arrProductsClass = lfGetProductsClass($product_id);

青い部分で、商品規格情報の取得を行っている。
lfGetProductsClass()関数に、何か秘密があると思った。

  そこでlfGetProductsClass()関数の定義部分を見てみる。

商品一覧の表示プログラム
lfGetProductsClass()関数の定義
(html/products/list.php)
/* 商品規格情報の取得 */
function lfGetProductsClass($product_id) {
    $arrRet = array();
    if(sfIsInt($product_id)) {
        // 商品規格取得
        $objQuery = new SC_Query();
        $col = "product_class_id, classcategory_id1, classcategory_id2, class_id1, class_id2, stock, stock_unlimited";
        $table = "vw_product_class AS prdcls";
        $where = "product_id = ?";
        $objQuery->setorder("rank1 DESC, rank2 DESC");
        $arrRet = $objQuery->select($col, $table, $where, array($product_id));
    }
    return $arrRet;
}
どうやら青い部分の「vw_product_class」テーブル(正確にはビュー:view)から
欲しい情報のフィールド(赤い部分)の値を取り出す。
その結果を配列に格納(ピンクの部分)し、返り値にしている事がわかる。

  では、「vw_product_class」ビューには、どんな情報が
載っているのだろうか。
 早速、フィールドを見てみる事にした。

vw_product_classビューについて
フィールド
product_class_id
product_id_sub
classcategory_id1
classcategory_id2
rank1
rank2
class_id1
class_id2
stock
price01
price02
stock_unlimited

(以下、省略)
中身について
登録された商品で、個々の規格別に分類された商品情報が入ったビューだ。

赤い部分は、その商品の「規格1」を示す。
青い部分は、その商品の「規格2」を示す。
共に規格名ではなく、規格IDが入る。
もし、その商品で規格別の分類を行っていない場合は
規格IDの部分は「0」が入る。

ピンクの部分は、その商品(該当の規格)の販売価格だ。

  そこで思った。

  lfGetProductsClass()関数で取得している「vw_product_class」ビュー情報で、
取り出す項目を増やせばええやん!

  だが、「vw_product_class」ビューには、規格1、規格2の規格名の
フィールドが設定されていない。
  そこで、規格情報が登録されているテーブル「dtb_classcategory」を
連結させたら良いと考えた。

vw_product_classとdtb_classcategoryを連結
vw_product_classとdtb_classcategoryを連結させた図式
上のように連結させる事によって、規格1の規格名と
規格2の規格名を取り出す事にした。

  そこで以下のようにプログラムに改造する。

商品一覧の表示プログラムを改造
lfGetProductsClass()関数の定義
(html/products/list.php)
/* 商品規格情報の取得 */
function lfGetProductsClass($product_id) {
    $arrRet = array();
    if(sfIsInt($product_id)) {
        // 商品規格取得
        $objQuery = new SC_Query();
        $col = "price02 , dtb_classcategory.name , aaa.name AS name2 ,product_class_id, classcategory_id1, classcategory_id2, class_id1, class_id2, stock, stock_unlimited";
        $table = "vw_product_class AS prdcls , dtb_classcategory , dtb_classcategory AS aaa";
        $where = "classcategory_id1 =  dtb_classcategory.classcategory_id and classcategory_id2 =  aaa.classcategory_id and product_id = ?";
        $objQuery->setorder("rank1 DESC, rank2 DESC");
        $arrRet = $objQuery->select($col, $table, $where, array($product_id));
    }

    return $arrRet;
}
SQL文の構成に手を加える事にした。

赤い部分は、取り出すフィールド名。
販売価格(price02)、規格1の規格名(dtb_classcategory.name)
規格2の規格名(aaa.name AS name2)を追加した。

そして、青い部分はテーブル名の指定。

ピンクは、テーブルから取り出す際の条件部分だ。

  これでSQL部分の変更は良し。

  次に、取り出したデータの処理部分の改造を行う。

  SQLの結果(返り値)は、lfMakeSelect()関数に戻る。
  なので、lfMakeSelect()関数を見ていく事にした。

  どうやら、この部分がSQLの結果を処理している所だ。
  だいたいの構造は以下のようになっている。

lfMakeSelect()関数の構造
(html/products/list.php)
function lfMakeSelect($product_id, $arrClassName, $arrClassCatName) {
    global $objPage;


    // すべての組み合わせ数   
    $count = count($arrProductsClass);

    for ($i = 0; $i < $count; $i++) {

    全ての組み合せの情報を取得
    それらを配列に格納
    }

配列に格納した情報を処理
}
個々の規格別が、どれくらいの数の組み合わせがあるのかを調べ
そして、個々の組み合わせごとの商品情報を取得。
それらを加工していく流れになっている。

  だいたいの眺めが見えた所で、改造していく事にした。

商品一覧の表示プログラムを改造
lfMakeSelect()関数の定義
(html/products/list.php)
改造前
// すべての組み合わせ数   
$count = count($arrProductsClass);

$classcat_id1 = "";

$arrSele = array();
$arrList = array();
改造後
// すべての組み合わせ数   
$count = count($arrProductsClass);

$classcat_id1 = "";

$arrSele = array();
$arrList = array();
$arrPrice = array();
$arrName = array();
個々の規格ごとの組み合わせ別の商品情報を格納する配列の初期化だ。
販売価格情報を格納する配列(赤い部分)と
規格1と規格2の規格名を格納する配列(青い部分)を追加する。

  そして、情報取得の部分に手を加える。

商品一覧の表示プログラムを改造
lfMakeSelect()関数の定義
(html/products/list.php)
改造前
for ($i = 0; $i < $count; $i++) {

(途中、省略)


// 規格2のセレクトボックス用
$classcat_id2 = $arrProductsClass[$i]['classcategory_id2'];
改造後
for ($i = 0; $i < $count; $i++) {

(途中、省略)


// 規格2のセレクトボックス用
$classcat_id2 = $arrProductsClass[$i]['classcategory_id2'];

// 明細を作成
$arrPrice[$classcat_id1][$classcat_id2] = $arrProductsClass[$i]['price02'] ;
if ($classcat_id2 == 0 )
   {
   $arrName[$classcat_id1][$classcat_id2] = $arrProductsClass[$i]['name'] ;
   }
else
   {
   $arrName[$classcat_id1][$classcat_id2] = $arrProductsClass[$i]['name']."(".$arrProductsClass[$i]['name2'].")" ;
   }
「// 規格2のセレクトボックス用」の行のすぐ後にプログラムを追加。

各規格別の価格と、規格名を配列に格納する部分だ。

  個々の規格ごとの商品情報を配列に格納できた。
  そして、次にlfMakeSelect()関数の返り値に、取得した情報を
覚えさせる処理だ。といっても、大した事はしない。

商品一覧の表示プログラムを改造
lfMakeSelect()関数の定義
(html/products/list.php)
改造前
   for ($i = 0; $i < $count; $i++) {

   (途中、省略)
   }

// 規格1
$objPage->arrClassCat1[$product_id] = $arrSele;
改造後
   for ($i = 0; $i < $count; $i++) {

   (途中、省略)
   }

// 規格1 価格 規格1の分類名
$objPage->arrClassCat1[$product_id] = $arrSele;
$objPage->arrClassPrice[$product_id] = $arrPrice;
$objPage->arrClassName[$product_id] = $arrName;
個々の組み合わせごとの商品情報を取得し、配列に格納した後
商品ID別に、取得した情報を格納していく。

  これでプログラム部分は完了。
  次にテンプレートの改造だ。

商品一覧の表示テンプレートを改造
(html/user_data/templates/list.tpl)
改造前
<td>
        <span class="red"><span class="fs12">価格</span><span class="fs10">(税込)</span></span><span class="redst"><span class="fs12">:
        <!--{if $arrProducts[cnt].price02_min == $arrProducts[cnt].price02_max}-->
                <!--{$arrProducts[cnt].price02_min|sfPreTax:$arrSiteInfo.tax:$arrSiteInfo.tax_rule|number_format}-->
        <!--{else}-->
                <!--{$arrProducts[cnt].price02_min|sfPreTax:$arrSiteInfo.tax:$arrSiteInfo.tax_rule|number_format}-->〜<!--{$arrProducts[cnt].price02_max|sfPreTax:$arrSiteInfo.tax:$arrSiteInfo.tax_rule|number_format}-->
        <!--{/if}-->
        円</span></span>
</td>
青い部分より上は、個々の規格があっても、価格が同じ場合の表示。
下は個々の規格ごとに価格が違う場合。
最安値と最高値だけが表示されるようになっている。
改造後
<td>
        <span class="red"><span class="fs12">価格</span><span class="fs10">(税込)</span></span><span class="redst"><span class="fs12">:
        <!--{if $arrProducts[cnt].price02_min == $arrProducts[cnt].price02_max}-->
                <!--{$arrProducts[cnt].price02_min|sfPreTax:$arrSiteInfo.tax:$arrSiteInfo.tax_rule|number_format}-->円</span></span>
        <!--{else}-->

                <table border>
                       <tr><td><span class="red"><span class="fs12">
			       <!--{$tpl_class_name1[$id]}--></span></td>
                            <td><span class="red"><span class="fs12">価格(税込)
			     </span></span></td></tr>


                        <!--{foreach from=$arrClassPrice[$id] key="key" item="datum" name="loop"}-->

                        <!--{foreach from=$datum key="key2" item="datum2" name="loop"}-->
                        <tr><td><span class="fs12"><!--{$arrClassName[$id][$key][$key2]}--></span></td><td><span class="fs12"><!--{$datum2|sfPreTax:$arrSiteInfo.tax:$arrSiteInfo.tax_rule|number_format}-->円</span></td></tr>
                        <!--{/foreach}-->
                        <!--{/foreach}-->
                   </table>
        <!--{/if}-->
</td>
<!--{else}-->以下の部分を書き換えた。
これによって、個々の規格名と価格が表示されるようになる。

  その結果は以下のようになった!

プログラム改造の結果
商品の展示画面 価格表の拡大
規格ごとに値段を表示 規格ごとの値段表示を拡大図
デザインセンスがないので、不格好になったのだが
これで商品の規格別の価格が表示でき、利用者には便利になる。

  ふぅ、見事、成功! (^^)V

  簡単に書き換えているように思われるかもしれませんが、
実際には、試行錯誤したりしています。
  でも、その様子まで書くと膨大になるので、割愛しました (^^;;


  これで規格別の値段表示ができたと思いきや、まだ不十分だ。

  「詳しくはこちら」で、商品の詳細を見る。

商品情報の詳細
商品の詳細の部分の値段表示も、わかりずらい
この部分も初期状態では「XX〜YY円」という表記になっている。
そのため個々の規格別の値段がわからない。

  ここも対応せんとアカンやん!

  なのだ。

  そこで、商品の詳細を表示させるプログラム(html/products/detail.php)や
テンプレート(html/user_data/templates/detail.tpl)を眺めて、
改造する事にした。


  基本的には、商品一覧の表示のlist.phpと同じ感じで改造すれば良い。

  まずは、個々の商品規格の情報を取得する関数である
lfGetProductsClass()に手を加える。

プログラムの改造部分
lfGetProductsClass()関数の改造前
(html/products/detail.php)
プログラムの改造前
/* 商品規格情報の取得 */
function lfGetProductsClass($product_id) {
        $arrRet = array();
        if(sfIsInt($product_id)) {
                // 商品規格取得
                $objQuery = new SC_Query();
                $col = "product_class_id, classcategory_id1, classcategory_id2, class_id1, class_id2, stock, stock_unlimited";
                $table = "vw_product_class AS prdcls";
                $where = "product_id = ?";
                $objQuery->setorder("rank1 DESC, rank2 DESC");
                $arrRet = $objQuery->select($col, $table, $where, array($product_id));
        }
        return $arrRet;
}
プログラムの改造後
/* 商品規格情報の取得 */
function lfGetProductsClass($product_id) {
        $arrRet = array();
        if(sfIsInt($product_id)) {
                // 商品規格取得
                $objQuery = new SC_Query();
                $col = "price02 , dtb_classcategory.name , aaa.name AS name2 , product_class_id, classcategory_id1, classcategory_id2, class_id1, class_id2, stock, stock_unlimited";
                $table = "vw_product_class AS prdcls , dtb_classcategory , dtb_classcategory AS aaa";
                $where = "classcategory_id1 =  dtb_classcategory.classcategory_id and classcategory_id2 =  aaa.classcategory_id and product_id = ?";
                $objQuery->setorder("rank1 DESC, rank2 DESC");
                $arrRet = $objQuery->select($col, $table, $where, array($product_id));
        }
        return $arrRet;
}
赤い部分は取り出すフィールドを追加した部分。
青い部分は、追加したテーブルだ。
ピンクの部分は、追加した条件の部分だ。

これで商品の個々の規格の価格と規格名1、規格名2が取得できる
SQL文になる。

  さて、SQL文の部分を改造した後は、SQL文を送り込んで
取得したデータの加工部分の改造になる。

  list.phpと同様、同じ商品で規格ごとのデータを取得して、
それらを処理する関数lfMakeSelect()がある。
  この関数の改造を行うため、関数の全体の構造を把握しておく必要がある。

プログラムの改造部分
lfMakeSelect()関数の定義
(html/products/detail.php)
/* 規格選択セレクトボックスの作成 */
function lfMakeSelect($objPage, $product_id) {
        global $objPage;

        for ($i = 0; $i < $count; $i++) {
                // 在庫のチェック

	    全ての組み合せの情報を取得
            それらを配列に格納

        }
配列に格納したデータの処理
}

  ほとんどlist.phpと同じ処理が行われる。
  list.phpと同様に、lfMakeSelect()関数の改造を行う。

プログラムの改造部分
lfMakeSelect()関数の改造前
(html/products/detail.php)
プログラムの改造前
// すべての組み合わせ数
$count = count($arrProductsClass);

$classcat_id1 = "";

$arrSele = array();
$arrList = array();
プログラムの改造後
// すべての組み合わせ数
$count = count($arrProductsClass);

$classcat_id1 = "";

$arrSele = array();
$arrList = array();
$arrPrice = array();
$arrName = array();
青い部分は規格別の価格を格納する配列の宣言
ピンクの部分は規格別の規格名1、規格名2を格納する配列の宣言

  そして、次にSQL検索で取得したデータ処理の部分に手を加える。

プログラムの改造部分
lfMakeSelect()関数の改造前
(html/products/detail.php)
プログラムの改造前
// 規格2のセレクトボックス用
$classcat_id2 = $arrProductsClass[$i]['classcategory_id2'];
プログラムの改造後
// 規格2のセレクトボックス用
$classcat_id2 = $arrProductsClass[$i]['classcategory_id2'];

// 規格別の価格と規格名を格納する処理
$arrPrice[$classcat_id1][$classcat_id2] = $arrProductsClass[$i]['price02'] ;
if ($classcat_id2 == 0 )
   {
   // 規格2が設定されていない場合
   $arrName[$classcat_id1][$classcat_id2] = $arrProductsClass[$i]['name'] ;
   }
else
   {
   $arrName[$classcat_id1][$classcat_id2] = $arrProductsClass[$i]['name']."(".$arrProductsClass[$i]['name2'].")" ;
   }
青い部分以下は、追加した部分です。
ここで規格別の価格と、個々の規格別の規格名を配列に格納する部分です。

  そして、テンプレートに送り込むためのオブジェクトに
取得した配列を覚えさせる必要がある。

プログラムの改造部分
lfMakeSelect()関数の改造前
(html/products/detail.php)
プログラムの改造前
// 規格1
$objPage->arrClassCat1 = $arrSele;
プログラムの改造後
// 規格1
$objPage->arrClassCat1 = $arrSele;
$objPage->arrClassPrice = $arrPrice;
$objPage->arrClassName = $arrName;
青い部分は、取得した規格別の価格の配列をSmartyに送り込む部分。
ピンクの部分は、取得した規格別の規格名の配列をSmartyに送り込む部分。

  これでプログラム部分の改造は良い。
  次にテンプレート(html/user_data/templates/detail.tpl)の改造を行う。

テンプレートの改造部分
(html/user_data/templates/detail.tpl)
プログラムの改造前
<--★価格★-->
        <span class="red"><span class="fs12"><--{$smarty.const.SALE_PRICE_TITLE}--></span><span class="fs10">(税込)</span></span><span class="redst"><span class="fs12">:
        <--{if $arrProduct.price02_min == $arrProduct.price02_max}-->
                <--{$arrProduct.price02_min|sfPreTax:$arrSiteInfo.tax:$arrSiteInfo.tax_rule|number_format}-->
        <--{else}-->
                <--{$arrProduct.price02_min|sfPreTax:$arrSiteInfo.tax:$arrSiteInfo.tax_rule|number_format}-->〜<--{$arrProduct.price02_max|sfPreTax:$arrSiteInfo.tax:$arrSiteInfo.tax_rule|number_format}-->
        <--{/if}-->
        円</span></span><br/>

        <--{if $arrProduct.price01_max > 0}-->
                <span class="fs12"><span class="red"><--{$smarty.const.NORMAL_PRICE_TITLE}-->:</span><span class="redst">
                <--{if $arrProduct.price01_min == $arrProduct.price01_max}-->
                        <--{$arrProduct.price01_min|number_format}-->
                <--{else}-->
                        <--{$arrProduct.price01_min|number_format}-->〜<--{$arrProduct.price01_max|number_format}-->
                <--{/if}-->
                円
                </span></span><br/>
        <--{/if}-->
プログラムの改造後
<!--★価格★-->

        <!--{if $arrProduct.price02_min == $arrProduct.price02_max}-->

        <span class="red"><span class="fs12"><!--{$smarty.const.SALE_PRICE_TITLE}--></span><span class="fs10">(税込)</span></span><span class="redst"><span class="fs12">:
        <!--{$arrProduct.price02_min|sfPreTax:$arrSiteInfo.tax:$arrSiteInfo.tax_rule|number_format}-->円</span></span>

        <!--{else}-->

        <!--規格によって値段が違う場合-->
        <br>

        <table border>
        <tr><td><span class="red"><span class="fs12"><!--{$tpl_class_name1}--></span></span></td>
        <td><span class="red"><span class="fs12"><!--{$smarty.const.SALE_PRICE_TITLE}-->(税込)
        </span></span>
        </td></tr>

        <!--{foreach from=$arrClassPrice key="key" item="datum" name="loop"}-->
 
                <!--{foreach from=$datum key="key2" item="datum2" name="loop"}-->
                <tr><td><span class="fs12"><!--{$arrClassName[$key][$key2]}--></span></td><td><span class="fs12"><!--{$datum2|sfPreTax:$arrSiteInfo.tax:$arrSiteInfo.tax_rule|number_format}-->円</span></td></tr>
                <!--{/foreach}-->
        <!--{/foreach}-->
        </table>

        <!--{/if}-->
        <br/>
青い部分から下を個々の規格別の価格と規格名が表示されるように
テンプレートの部分を変更した。

  さて、プログラムとテンプレートを変更した結果

  見事、成功 (^^)V

  だった。

商品情報の詳細
商品詳細画面での規格ごとの値段表示 商品詳細画面での規格ごとの値段表示の拡大図
商品の規格ごとに価格と規格名が表示されるようになった。
これで利用者には見やすくなった。

  オープンソースなので、必要に応じて、自由に改造ができる。
  これは大きな利点だと思う。

EC-CUBEの開発者に会う ちょこちょことEC-CUBEを触っている時、2007年10月26日、 ロックオン社のEC-CUBEのセミナーが大阪で開催された。 セミナーと懇親会に参加した私。 懇親会の時、ロックオン社のEC-CUBEの開発者の方と、ワイワイお話をする 機会があった。 元々、有償だったソフトを、突然、社長の岩田さんの鶴の一声で オープンソースにした話や、企業である以上、健全な企業活動を行う上で、 収益を考える必要があるため、他の案件を手がける都合上、 EC-CUBEばかりに労力を投入できない現実的な話も聞いた。 懇親会でロックオン社の方々とお話をして、わかった事は、 EC-CUBEに凄く愛着を持ち、EC-CUBEが成長し、世に広まっていく事を願って 開発している姿を、非常に感じた。 特に、大阪発のオープンソースという事もあり、関西人である私は EC-CUBEの発展を応援したいし、もし、この奮闘記が、 少しでもEC-CUBEの宣伝になればと思った。
ついにネット販売システム刷新 2008年1月7日、めでたくネット販売の刷新ができた。 2007年3月に刷新の話が出てから、紆余曲折しながらも なんとか刷新できた。 既に、EC-CUBEのバージョンも、1.4.5や2.0が出ていたりする。 EC-CUBEを発見してから導入まで半年以上かかってしまった。 でも、安全にEC-CUBEを導入する上で、止む得なかった。 リスク回避のために、未開封のままにしていたデータベースなどの パンドラの箱を開けていったためだった。 EC-CUBEのプログラムを読みながら、それにヒントを得て、 うちの会社のシステムのソースを書き替えたりして、 うちの会社のシステムの改良にもつながった。 時間はかかったが、私個人や、うちの会社にとっては、 良い意味での波及効果があった。
最後に 日本発、大阪発のオープンソースのECサイト。EC-CUBE。 日本発という事もあり、日本の商習慣に合わせた機能を持っています。 その上、Smartyを使っている事もあり、PHPとHTMLが分離されていて、 改造しやすい点が大きな利点だと思います。 PostgreSQLとMySQLの両方でも使えるのは、凄くありがたいです。 私自身、全てのソースコードを把握したわけでもないし、 全ての機能を理解したわけでもないため、EC-CUBEの魅力を伝え切れないですが、 こんな便利な物をオープンソースで使えると思うと、ロックオン社の方々に ありがたさを感じます。 岩田さんを始め、ロックオン社の方々には感謝しています。 ロックオン社 http://www.lockon.co.jp/ EC-CUBEのサイト http://www.ec-cube.net/

次章:「日本発のWebファイルマネージャー axlope」を読む
前章:「Linux ApacheでSSL暗号化通信のインストール設定」を読む
目次:Linux、オープンソースで「システム奮闘記」戻る

Tweet