カラーミーショップのカスタマイズに便利なSmartyを学ぶ (2/2)

Smarty公式ページ

Smarty解説の第二回目になります。
前回は、独自タグの役割(変数、制御構文)について解説しました。


今回は実践編として、実際にどのようなときに使えるか(または使っているか)、例を挙げながら紹介します。

カラーミーショップで、レイアウトや表示データを加工したいといった際に、Smartyの機能を使って実現可能な場面が結構あります。

0. Smarty公式は必要に応じて目を通します

Smarty構文は見慣れるとなんとなく書けるようになりますが、より深く理解するにはSmarty公式のマニュアルの通読は必須です。
知らないことがさらっと書いてあります。ヒントを探す際にも目を通しましょう。

Smarty公式のデリミタは{ }ですが、テンプレートにJavaScriptやCSSが含まれる場合に、Smartyの構文解釈に不都合が生じます。
カラーミーショップではテンプレートにJavaScriptやCSSが含まれる場合をあらかじめ考慮して、デリミタは<{ }>になっています。
Smarty公式の例を参考にする場合は読み替える必要があります。

1. sectionの変数を使う

前回にすこし触れましたが、sectionは繰り返し文(ループ文)のSmarty構文です。
このsectionループ内では、section自身の変数(プロパティ)にアクセスできます(ループに関連した変数があらかじめ用意されているんですね)。

下例では$smarty.section.num.iterationという風に書いて、ループした回数を参照しています。

sectionの変数(.iteration)を使う例

<{section name=num loop=$seller}>
  <li class="col col-sm-4 col-lg-3 seller_list<{if $smarty.section.num.iteration > 6}> hidden-phone<{/if}>">
 (売れ筋商品を表示しています 中略)
  </li>
<{/section}>

ループ回数が6を超えたらhidden-phone(カラーミーキットであらかじめ用意されているクラス名)を付与しています。

スマホ閲覧時には商品点数が6を超えたらそれ以降は非表示にするという内容になります(長々と商品表示しないという小画面デバイス閲覧時の配慮)。

sectionの話からは逸れますが、HTMLのクラス名を書いているところにSmartyで条件文を書くと、条件分岐でデザインを変えたり、スタイルを変えたりできるわけです。
条件文が商品一覧ページなら、<{if $tpl_name == "product"}> product_style<{/if}>といった風に。

.iteration以外では、.first、.lastも見かけます。

下例は商品一覧などで商品画像を3つ横並びにする場所に書いてあるコードです(古めのテンプレートでよくみかけます)。

sectionの変数(.last)を使う例

<!--商品を3個で改行(最後は含まない) ※4個で改行にする場合は"% 3"を"% 4"に変更してください-->
<{if $smarty.section.num.iteration % 3 == 0 and $smarty.section.num.last == false}>

%は剰余を求める演算子(+,-と同類)。ループ回数(iteration)が3の時は余りが0で、かつループが最後(last)でなければ、条件は満たされます。

次は、ループの最初に<div>を出力して、ループの最後に</div>を出力する例です。
2行目に.first、6行目に.lastが使われています。

sectionの変数(.first .last)を使う例

<{section name=num loop=$group}>
  <{if $smarty.section.num.first }>
    <div>
  <{/if}>
  (中略)
  <{if $smarty.section.num.last }>
    </div>
  <{/if}>
<{/section}>

2. Smartyの変数

Smartyの変数には、あらかじめ用意されている変数(sectionの変数など)と自ら割り当てる変数があります。
ここでは、sectionの変数以外についても紹介します。

予約変数

sectionループ内で使える変数以外にもあらかじめ用意されているものがあります。
$smarty. が先頭についている変数を予約変数といいます。
下に例を挙げます。

リクエスト変数を取得する例

<{if $smarty.get.pid == '12345678'}>
  (中略)
<{/if}>

ある商品の商品詳細ページのURLがhttp://www.example.com/?pid=12345678だったとします。

?以下はリクエスト変数といって、サーバー(カラーミーショップ)に渡すパラメーターとして機能しています。
具体的には、クライアント(閲覧者)が商品ID 12345678のページをリクエストして、サーバー(カラーミーショップ)は、パラメーターを受け取って、ページを返します。

例では、商品IDが12345678の場合に何かを表示させるというような条件分岐に使えます。

リクエスト変数を取得する例をもう一つ挙げます。
今度はパラメーターが複雑になっています。?以降がサーバーに渡すパラメーターです。
http://www.example.com/?mode=cate&cbid=123456&csid=1&sort=pだったとします。
cbidはカテゴリー大、csidはカテゴリー小に振ってあるカテゴリーIDです。太字に注意してみて下さい。

カテゴリー大を条件にする場合は下記のとおり。

リクエスト変数を取得する例

<{if $smarty.get.cbid == '123456'}>
  (中略)
<{/if}>

同様に、条件文をフリーぺージにしたいなら、<{if $smarty.get.mode == 'f1'}>、グループなら、<{if $smarty.get.gid == '9876543'}>のようになります。

予約変数でその他には、前回の、$smarty.section.num.iterationが予約変数です。

現在の日時が取得できる$smarty.nowも、期間限定で表示させるバナーなど、$smarty.nowの条件文で設置することが可能です(Smartyを知らなければ、JavaScriptで書いてしまいますが)。

曜日は英語(Sunなど)で出力されますので、表示形式にあわせて置換します。

サーバー時間を取得する例

<{$smarty.now|date_format:'%Y-%m-%d %H:%M:%S'}>

<{$smarty.now|date_format:'%Y年%m月%d日(%a)'|replace:'Sun':'日'|replace:'Mon':'月'|replace:'Tue':'火'|replace:'Wed':'水'|replace:'Thu':'木'|replace:'Fri':'金'|replace:'Sat':'土'}>

予約変数以外であらかじめ用意されている変数

具体的には、独自タグがそうです。
たとえば、カラーミーショップでは<{$productlist[num].price}>と書くと商品の定価を出力できます。

あらかじめサーバー(カラーミーショップ)側のプログラムで、私たちがテンプレートをカスタマイズしやすいように変数を割り当てて、中身を渡すように用意してくれているのです。

ちなみに、独自タグには変数のスコープがあるので注意が必要です(特定のページでしか使用できないことがよくあります)。

テンプレート内で自ら割り当てる変数

テンプレート内でassign関数を使って、自ら割り当てることもできます。これは4章で説明します。

3. データ加工する際に使う修飾子

変数(独自タグなど)を加工して出力したい場合がよくあります。
たとえば、商品名を先頭から何文字分切り出す(truncate)、ある文字列を別の文字列に置換する(replaceregex_replace)など。

その他の修飾子Smarty公式のマニュアル参照。修飾子をパイプ(|)でつなげて書いていきます。

リクエスト変数を取得する例

<!-- 商品名を先頭から30文字に短縮する -->
<{$product.name|truncate:30}>

<!-- 販売価格についている不要文字"(税込)"を削除するする -->
<{$product.sales|regex_replace:"/\(税込\)/":""}>

<!-- パイプでつないで修飾子を複数書けます -->
<{$input|escape:'html':'UTF-8'|nl2br}>

strip_tags

$product.nameから商品名だけを取り出す

<{$product.name|strip_tags}> 

独自タグ<{$product.name}>には商品名+付加画像(new!とか)がセットされています(付加画像を使用するの場合)。<img src="~" />のようなHTMLタグを取り除いて、簡単に商品名だけにできます。

replace

replaceは、左の文字列を右の文字列に置換します。置換文字列の条件が簡単な場合に使えます。

独自タグには、使う場面によっては余分なものがついていたりします。
よくある例は販売価格に、円(税込)が付く場合です。

<{$product.sales}>から「円(税込)」を取り除く

<{$product.sales|replace:"円(税込)":""}>


付加画像を自分の画像に差し替える場合も、replaceでできます。

付加画像を自分の画像に差し替える

<!-- newアイコンを別のものに差し替える -->
<{$product_name|replace:'https://img.shop-pro.jp/img/new/icons1.gif':'https://your.com/new.png'}>


商品詳細ページには大カテゴリーの独自タグがありません。
外部サービスを使うときに必要だったりしますし、カラーミーショップAPIを併用すれば同カテゴリーの一覧を下に表示したりできます。

パンくずの独自タグ<{$bid_link}>(大カテゴリーURL)から切り出して、大カテゴリーIDを取得します。

大カテゴリーIDを取得する

<{$bid_link|replace:"?mode=cate&cbid=":""|replace:"&csid=0":""}>

regex_replace

regex_replace正規表現を使って文字列を置換します(正規表現の書き方は難しいですが、検索すると案外使いたいものが見つかります)。置換文字列の条件が複雑な場合に使います。

加工してすぐに条件文で使ったりできます

<{if $recommend[num].name|regex_replace:'/検索文字列/':'x' ne $recommend[num].name}>
(検索文字列があった場合の処理)
<{else}>
(検索文字列がなかった場合の処理)
<{/if}>

$recommend[num].name(おすすめ商品の商品名)の中に検索文字列があればxに置換し、元の商品名と比較しています。neは条件文で使われる条件演算子(等しくないの意味)。

元の商品名と一致しない場合(すなわち、商品名の中に検索文字列があった場合)は、検索文字列があった場合の処理を行います。
「検索文字列をxに置換して、元の文字列と比較」というように回りくどい方法ですが、Smartyではこんな風にして検索文字列の有無を判断します。

商品名の中に、色、形状、サイズ、タグなどをあらかじめ仕込んでおくと、ある商品特性を持つものだけ〇〇する、というような条件分岐ができます。

imgタグを出力する独自タグでalt属性が付いていないものがあります。regex_replaceを使えば「alt="商品名"」を差し込んだりすることも可能です。

4. 知っておくと便利そうな関数

あまり気にしなくてよいのですが、ifやsectionの制御構文は、Smartyでは関数扱いなんだそうです。
Smartyにおける関数という用語は、プログラミング言語一般のものとは違うなぁという感じ。

その他関数で使いそうなものは、includeassignmath、fetch※などです。

※ カラーミーショップでは、2019年1月21日以降、fetchが使えなくなりました。代替手段の一つとしてiframeを挙げています(このページの一番最後を参照)。

include(他のテンプレートをインクルードする)

includeはテンプレート(共通HTML)内で使われています。下例ではテンプレート(共通HTML)の中から、他のテンプレートをインクルード(差し込み)しています。

クライアント(閲覧者)からのリクエスト変数を見て、サーバー(カラーミーショップ)がどのテンプレートを出力するか判断して、$file_nameを渡しています。

?pid=ならテンプレート(商品詳細HTML)をインクルードしますし、パラメーターがなければ、テンプレート(トップHTML)をインクルードします。

テンプレート(共通HTML)内で

<{include file = $file_name}>

比較的新しいテンプレートでは、トップページかトップページ以外かで出力を変えている場合も見かけます。実際にはHTMLタグがごちゃごちゃと入って、非常に読みにくいものになりますが、構造部分はこんな感じ。

テンプレート(共通HTML)内で見かける最近の例

<{if $tpl_name == "top"}>
  <!--トップページ-->
  <{include file = $file_name}>
  (トップページ専用のコード)
<{else}>
  <!--トップページ以外-->
  <{include file = $file_name}>
  (トップページ以外のコード)
<{/if}>

同様に、フリーページかどうかで出力を変える例

<{if $tpl_name|truncate:4:"" == "free"}>
  <!--フリーページ-->
  <{include file = $file_name}>
  (フリーページ専用のコード)
<{else}>
  <!--フリーページ以外-->
  <{include file = $file_name}>
  (フリーページ以外のコード)
<{/if}>

assignとmath(変数の割り当てと計算)

assignは変数を割り当てられ、mathは計算が実行できます。
JavaScriptなどを使わずに、テンプレート内で変数を割り当てたり、計算することができます。

下例は販売価格の5%をポイントとして出力しています(以前はカラーミーショップにポイント表示のための独自タグがなかったので、このようにSmartyで計算して出力していました)。

$product.salesに含まれる、円や(税込)のような不要な文字列を取り除いて数字のみにして、それを計算式に入れてポイント計算しています。

販売価格の5%をポイントとして表示する例

<!--ポイント支給率 100円毎に5ポイントの場合-->
<{assign var="sData" value=$product.sales|regex_replace:"[[\(]+[\S]+[\)]]":""}>
<{assign var="sData" value=$sData|replace:",":""}>
<{assign var="sData" value=$sData|replace:"円":""}>
<{math equation="floor($sData/100)" assign="sData"}>
<{math equation="round(5*$sData)" assign="uData"}>
<{$uData}>ポイント

PHPの関数を使う

Smartyの関数以外にも、「全ての PHP 関数は暗黙で修飾子として使用する事ができます」とマニュアルに記述がありますが、実際にはセキュリティ上の理由でごく一部に限定されています。
参考)Smarty - 変数の修飾子

count

カラーミーショップでは、PHPの関数count()が使用されているのをよく見かけます。
配列の要素数を数えるときに使っています。

修飾子のようにパイプでつないで使っています。

count関数を実行する例

<!-- 登録されたグループが0でないなら -->
<{if $group|@count != 0}>

<!-- オプションが2点以上あるなら -->
<{if $option|@count > 1}>

var_dump

PHPのvar_dump関数は使えませんが、Smartyのdebug_print_varで配列の中が見られます
独自タグのマニュアルにない項目(変数)を調べる際に使います。

<{$group|@debug_print_var:0:100}>の出力結果

Array (2)
0 => Array (5)
  id => "1961614"
  link_url => "https://naeco.shop-pro.jp/?mode=grp&gid=1961614"
  img_url => ""
  name => "グループ"
  level => "1"
1 => Array (5)
  id => "1961624"
  link_url => "https://naeco.shop-pro.jp/?mode=grp&gid=1961624"
  img_url => ""
  name => "グループ4"
  level => "1"

5. Smarty解説のまとめ

Smartyで学ぶべき箇所は大きく分けて、制御構文、変数、修飾子・関数となります。
それに加えて、カラーミーショップの独自タグを理解すれば、使いこなせるようになります。

プログラミング経験があるとずいぶんと理解がしやすくなります(特にPHPを学んでいると、関連で理解が進みやすい)。

旧ブログのほうにもサンプルをいくつか載せてあります。

補足 カラーミーショップのfetch禁止

カラーミーショップのオーナー様へのお知らせで告知されたように、今後はfetchが利用できなくなります。
fetchで動的に生成されるページを取得していた場合は、代替手段としてiframeを提案してくれています。

・CMSやRSSなど、動的に生成されるページを取得している場合
iframeタグで読み込むことでショップ内にページを表示させることが可能となります。

参照元:2018/06/27 - デザインテンプレートで一部の記述が使えなくなります(要ログイン)

iframeで上手く読み込めない場合の原因としては、混在したコンテンツ(SSL絡み)、サーバーの設定(X-Frame-Options)などが考えられます。

iframe以外では、ブログFeedをAjax(非同期通信)でやり取りする方法もあります。非同期通信なので、ページ読み込み時にロックしないので、利用者のストレスは減ります。

執筆者

えいじ@naeco.jp この記事を書いた人

メーカー系情報システム部門出身の個人事業主。
自作するのが好きですぐに試したくなる、凝り性なWebエンジニア。
カラーミーショップ、モールなどのECについて記事にしています。

ご相談・お問い合わせはこちら