color me shopにページャーをつける

color me shopをいじる機会があったので、今回作ったページャーをご紹介。
テンプレートは、smartyなので馴染みがあり、やりやすかったです。

目指す表示はこんな感じ
<< 前のページへ 1 ・・ 4 5 6 ・・・ 10 次のページへ >>

   
<{* 現在表示中のページ数をcurrent_pageに設定 *}> <{ if $smarty.get.page == "" }><{ assign var="current_page" value="1" }><{else}><{ assign var="current_page" value=$smarty.get.page }><{/if}> <{* 前のページがある場合は、リンク化する *}> <{if $productlist_prev_page != ""}><< 前のページへ<{else}> << 前のページへ <{/if}> <{* ページ表示数で商品数を割り、ページ数を算出 *}> <{math equation="ceil($productlist_num/12)" assign="max_page"}> <{math equation="$current_page-1" assign="bef_page" }> <{math equation="$current_page+1" assign="aft_page" }> <{ assign var="page_flg" value="0" }> <{section name=pager loop=$max_page }> <{* 表示中のページに、専用のclassを付与する *}> <{ if $smarty.section.pager.iteration == $current_page }> <{$smarty.section.pager.iteration }> <{ else }> <{ if $productlist_sort_now == "p" }> <{ assign var="page_url" value=$productlist_sort_price|cat:"&page="|cat:$smarty.section.pager.iteration }> <{ elseif $productlist_sort_now == "n" }> <{ assign var="page_url" value=$productlist_sort_new|cat:"&page="|cat:$smarty.section.pager.iteration }> <{ else }> <{ assign var="page_url" value=$productlist_sort_def|cat:"&page="|cat:$smarty.section.pager.iteration }> <{ /if }> <{ if $max_page > 7 }> <{* 1ページ目と最終ページ、現在表示しているページの前後1ページをリンクとして表示 *}> <{ if in_array($smarty.section.pager.iteration, array(1,$bef_page, $aft_page, $max_page)) }> <{ assign var="page_flg" value="0" }> <{ $smarty.section.pager.iteration }> <{ elseif $page_flg == "0" }> <{ assign var="page_flg" value="1" }> ・・・ <{ /if }> <{ else }> <{ $smarty.section.pager.iteration }> <{ /if }> <{ /if }> <{/section}> <{* 次のページがある場合は、リンク化する *}> <{if $productlist_next_page != ""}>次のページへ >><{else}> 次のページへ >> <{/if}>
color me shopにページャーをつける

Zend_ViewでSmartyをラップする6

今回は、やりたいことの2つ目、3つ目に入っていた
2.プログラムが必要ない場合は、Actionコントローラーを書きたくない
 (Actionメソッドがなくても、テンプレートが表示されるようにしたい)
3.テンプレートもないURLが指定されたら、トップページなど指定のURLに遷移させたい
を実現する方法です。

Zendは、初期状態で、ErrorHandlerプラグインというプログラムが有効になっています。
これは、プログラム実行中に、エラーが発生した場合、
ErrorActionコントローラーのerrorActionというメソッドをよびだしてくれます。

そこで、コントローラークラスが見つからなかった場合、
アクションメソッドが見つからなかった場合の判別が可能です。
私が作った、ErrorActionクラスから必要な部分を抜き出したのが以下です。

class ErrorController extends Zend_Controller_Action
{
 public function errorAction()
 {
  $errors = $this->_getParam('error_handler');

  switch ($errors->type)
  {
   case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER:
    if ($this->_getParam('action') == 'index')
    {
     $view = $this->getHelper('viewRenderer');
     $view->setNoController()
         ->setScriptAction($this->_getParam('controller'));
    }

   case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION:
    if (!isset($view))
    {
     $view = $this->getHelper('viewRenderer');
     $this->getRequest()->setControllerName($this->_getParam('controller'));
     $this->getRequest()->setActionName($this->_getParam('action'));
    }

    $template = $view->getViewBasePathSpec() . $view->getViewScript();
    if (!is_readable($template)) $this->_redirect('/');
    break;

   default:
    break;
  }
 }
}
Zend_ViewでSmartyをラップする6

Zend_ViewでSmartyをラップする5

前回で、Smartyの利用と、ViewRendererヘルパーを利用して、
Smartyを利用した画面の自動描画が有効になる方法を紹介しました。

今回は、やりたいことの1つ目にあげていた
1.テンプレートの配置をURLからイメージしやすいようにしたい
ということを実現する方法です。

この方法を実行するのは、各コントローラーのindexActionのみです。
そこで、このどちらかを記述すればOKです。

$this->getHeler('viewRenderer')
   ->setNoController()
   ->setScriptAction($this->getRequest()->getControllerName());

または

$this->render($this->getRequest()->getControllerName(), null, true);

これはどちらも同じ意味になります。

こうすることで、
http://test.com/aaa/ というURLでアクセスされた場合、
templates/aaa/index.htmlではなく、templates/aaa.htmlを読み込んでくれます。

Zend_ViewでSmartyをラップする5

Zend_ViewでSmartyをラップする4

前回の記事で書いたことを試してみました。
私がsmartyとZendを合わせて使う上で、実現したいのは以下

1.テンプレートの配置をURLからイメージしやすいようにしたい
 http://test.com/aaa/ の場合は、templates/aaa.html
 http://test.com/aaa/bbb/の場合は、templates/aaa/bbb.html

2.プログラムが必要ない場合は、Actionコントローラーを書きたくない
 (Actionメソッドがなくても、テンプレートが表示されるようにしたい)

3.テンプレートもないURLが指定されたら、トップページなど指定のURLに遷移させたい

1.は、renderのnoControllerがtrueのときのテンプレート作成ルールを、
コントローラー名+指定拡張子とすることで解決。
ただし、render呼び出しの際に、
$this->render(コントローラー名, null, true)
とする必要がありますが、この部分を簡略化できないかが今後の課題です。

2.はまだ試せてません。しかし、前々回の記事の方法が応用できそうです。
3.も2.と同様に前々回の記事の方法で実現できそうです。

しかし、前回と同様の方法だと、Zend_Controller_Actionのサブクラスを基底クラスとして
作成することになるので、これはできれば避けたいところです。
会員サイトとかなら、基底クラス作るのはいいのですが、Smarty使いたいだけで、
基底クラスを作るとなると、ログインセッション管理をやろうと思ったときに、
テンプレートエンジンに関わるファイルを修正することになりかねません。

index.phpにつぎたした部分は以下です。

require_once('Zend/Controller/Action/Helper/ViewRenderer.php');
require_once('Custom/View/Smarty.php');

$view = new Zend_View_Smarty(null, $smartyConfig);

$viewHelper = new Zend_Controller_Action_Helper_ViewRenderer($view);
$viewHelper->setViewBasePathSpec($smartyConfig['template_dir'])
      ->setViewSuffix('html');
Zend_Controller_Action_HelperBroker::addHelper($viewHelper);

$smartyConfigは、Smartyの設定値の配列です。
Custom/View/Smarty.phpは、Zend_View_Interfaceに沿って、
Smartyが使えるようにしたViewクラスです。
中身は、ここの最後の方にのっているSmartyラッパークラスと同様です。
ただ、function getScriptPaths()に誤りがあります。

return $this->_smarty->template_dir;
  ↓
return array($this->_smarty->template_dir);

とする必要があります。
getScriptPaths()なので、パスが配列で複数取得されることが想定されて、
Zend_Controller_Action_Helper_ViewRendererが作成されているためです。

これでより少ないソースで、smartyと連携できるようになりました。
より手間を少なくする方法がないか、もうちょっと試してみる予定です。
ViewRendererヘルパーと、ErrorHanderプラグインあたりを組み合わせると、
なんとかなりそうな気がドキュメントを読んでいるとするのですが、どうなんでしょう。
あたりがついたら、試してみます。

Zend_ViewでSmartyをラップする4

Zend_ViewでSmartyをラップする3

昨日、ActionコントローラのinitViewをオーバーライドが簡単って
書いたばかりですが、こんなドキュメントを見つけました。
別件でヘルパーかプラングインを作ってみようとしてたときです。

ここの【例 7.10. パスの指定方法の変更】の部分です。

テンプレートエンジンをSmartyにするだけなら、これがより簡単です。
見つけたとき、軽く衝撃でした。

Smartyを使いつつ、私がやりたいこと(現在の方法で実現していること)が
この方法を使ったときに実現できて、かつ簡単なら乗り換えようかと。

結果は、またこのブログに書きたいと思います。

Zend_ViewでSmartyをラップする3

Zend_ViewでSmartyをラップする2

ドキュメント読んだり、ソースおったりしているうちに、
Zend_Controller_ActionクラスのinitViewをオーバーライドして、
Front Controllerにて、noViewRendererをtrueに設定して、
各アクションの最後に、$this->render()で、画面表示するのが、
一番楽なんじゃないかという結論に達しました。

重要なのは、initViewの中で、$this->viewに、Zend_View_Interfaceに
沿って作成したSmartyクラスを設定すること。
これがあって初めて、既存の$this->render()などが利用できます。

なぜこの方法にいきついたかの理由は、デフォルトのテンプレートエンジンである
Zend_Viewを利用する際には、ほとんど利用されていないメソッドだからです。
Zend_Viewを利用する場合、Zend_Controller_Action_Helper_ViewRendererという
ヘルパークラスが利用されています。

Frontコントローラーで、noViewRendererが設定されるなどして、
自動でレンダリング(画面描画)する機能がOFFにされていない限り、
上記のヘルパーを介して、Zend_Viewクラスを利用しています。
Zend_Controller_Actionの中の以下のメソッドでは、
上記ヘルパー内の同名のメソッドを呼び出します。

  ・render
  ・renderScript
  ・getViewScript

initViewに関しては、Actionクラスの生成時に同時に生成される
上記ヘルパークラスを、リターンするという内容になっています。

noViewRendererがONになっているときに、これらのメソッドを自分でプログラムに記述し、
Zend_Viewクラスを生成して、画面描画することもできますが、Zend_Viewを使うなら、
自動で行ってくれる処理を、なぜ手動にするか理由が見当たりません。
(自動描画は、必要な時だけOFFにすることもできるのです)

なので、別のテンプレートエンジンを利用するときに、オーバーライドするなら、ここかなと思ったのです。
ヘルパーを作るっていう手段もありますが、オーバーライドした方が簡単そうだったからです。
そのうち、勉強をかねて、ヘルパーを作る方法を試すかもしれません。

Smartyを利用することで、テンプレートの拡張子もデフォルトの”.pthml”でないものを
使いたい方もいるでしょう。その場合は、Actionクラス内のpublic変数である $viewSuffixを
書き換えることで変更可能です。

initViewを再定義した場合、Actionクラスのinit()などで呼び出さないと、
renderするタイミングまでインスタンスが生成されないため、テンプレートに
値を設定することができませんので、お忘れなく。

Zend_ViewでSmartyをラップする2

Zend_ViewでSmartyをラップする

これを知るまで、Contorollerで、Smartyクラスを生成して使っていたけど、
Zend_Viewのドキュメントに記載されていた、Smartyをラップする
Zend_Viewクラス(ex Zend_View_Smarty)を作成して、これを利用することで、
Smartyの便利さはそのままに、テンプレートへの値の設定がさらに楽に!!!

簡単に言うと、$key っていう変数に、$valueを設定しようとした場合、
$this->_smarty->assign($key, $value);
と書いていたのが、
$this->_view->$key = $value;
ってかけるようになるんです。
assignの記述で、よくスペルミスしていた私には、重宝する機能です。

smartyの設定(delimiterとか)も、連想配列にして、Zend_View_Smartyクラスの
生成時に、第2パラメタとして渡したら有効になります。
テンプレートへのパスも、setScriptPathではなく、生成時に設定した方が楽です。

すでに、Zendで、Smartyを使っている方でも、変更量は少なくてすみますので、
試す価値有りと思った方は、ぜひおためしくださーい。

この方法を採用していると、テンプレートエンジンを変更しようとおもった際の
ソースの改修もラッパークラスを変更するだけですみます。
テンプレートは大幅な改修になることは、避けられませんが。

DB操作にしても、変数に代入するイメージで、設定できるっていう
Zendの特徴を生かしたSmartyとの連携方法だと思います。

これをやったら、既存のZend_Viewのように、Zend Frameworkが、
Zend_View_Smartyを生成して、テンプレート特定して、画面表示までを
やってくれないかなーという欲がでてきました。
Zend_Controller_Actionを継承して、initViewあたりを再定義したら、
できそうだけど、他にもっと楽な方法がないか探して見ます。

存在しない、URLが指定された場合、action名の方は任意の処理を
入れれる方法がわかっているのですが、controller名が存在しない場合は、
エラーになるだけなので、ここをどうにかしたいところです。

Front Controllerを継承したクラスを作るのはちょっとおっくうなので、他の方法で。

Zend_ViewでSmartyをラップする