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_Mail)

以前の記事で、Zend_Mailを使って、メールサーバにSMTP認証し、
メールを送信する方法を書きましたが、そこの記述で問題がある部分がありました。

Zend_Mailを生成する際に、パラメタとして ‘UTF-8’を設定しています。
これでも、正常に送信できることはできるんですが、件名が日本語で、
かつ、ある一定以上の長さになった場合に、途中で切れてしまう現象が発生しました。

Zend_Mailのソースを追おうかと思いましたが、面倒なのでやめました。

件名、本文を、mb_convert_encodingで、’ISO-2022-JP’にエンコードし、
Zend_Mailを生成する際のパラメタも同じく ‘ISO-2022-JP’にすることで、
件名が途中で切れる問題は回避できました。

結局、エンコードが必要なのかって思ってしまいますが、
大した手間ではないし、いいかなと。

=====

解決と思ったら、今度は送信者名の末尾に文字化けのような文字がついてしまいました。
Zend_Mailのパラメタを’UTF-8’にしていなかったときは、化けていなかたのに。
送信者名は必ずつけたいというわけではないので、
削除してISO-2022-JPで、今回はいくことにします。

解決策をご存じの方は、ぜひコメントください。助かります。

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_Config_Iniの使い道

Zend_Config_Iniは、設定値や固定のプルダウンメニューリストを定義するのに便利です。

まずは、iniファイルを定義します。

[default] ←ブロック名

database.adaptor         = Pdo_Mysql
database.config.host     = localhost
database.config.username = user
database.config.password = password
database.config.dbname   = test

menu.0        = menu0
menu.1        = menu1
menu.2        = menu2

それをプログラムにて、Zend_Config_Iniクラスを生成する際に読み込みます。

$config = new Zend_Config_Ini(iniファイル名(パス含む), 'default');

$config->database->adaptor

で、”Pdo_Mysql”にアクセスできます。
また、$config->database->config->toArray()とすると、

array(
'host' => localhost,
'username' => user,
'password' => password,
'dbname'   => test
);

が取得できます。

また、menuの例だと、
$cofig->menu->0
とアクセスすると、エラーになります。
数字1文字は変数名として認められないようです。
しかし、$config->menu->toArray()は有効になるので、
添え字が数字の固定配列を定義するのには使えます。
これは、結構便利なんじゃないかと思います。

今は、Zend_Validateクラスを試しています。
自分で入力チェックするのと手間はあんまり変わりませんが、
バリデータチェインは便利ですね。

今回のドキュメントは、こちらです。

Zend_ControllerとRequestクラス

Actionコントローラーの中で、
$this->getRequest()
と書くと、リクエストオブジェクトが取得できる。

$_POST、$_GETの情報も、Controller名、Action名もこの中。

$this->getRequest()->getPost();
$this->getRequest()->getQuery();
$this->getRequest()->getControllerName();
$this->getRequest()->getActionName();

$_GETは、ちょっとメソッド名がイメージしづらいけど、ここは覚えるのみ。
getPost()、getQuery()は、パラメタにキーを渡すと、キーに対応する値を取得してくれる。

それ以外にも、setParam($key, $value)で、リクエストオブジェクトに値を設定可能。
これは、いつでもgetParam($key)で取得可能。
getParam()だと、$_POST、$_GETのパラメタも取得可能ですが、
getUserParam()だと、setParam()で設定した値のみが取得可能。
ここは使い分けた方が、プログラムの可読性が増すかなと思います。

あと、発見だったのが、Zendでは、
http://test.com/controller名/Action名/key/value/ 【1】
というようにパラメタを渡せます。
http://test.com/controller名/Action名/?key=value 【2】
としても渡せます。

この場合、【1】の場合は、keyはsetParamで設定したものと同じように扱われおり、
【2】の場合は、$_GETに設定されています。
パラメタの記述方法で、保存される場所が変わるので、気をつける必要があります。

次回は、便利だなと思った。Zend_Config_Iniについて書こうかなと思います。

今回書いたことは大体、ZendFrameworkのドキュメントに書いてあります。
ドキュメントは、こちらです。

ZendとcakePHP

Zendで、メール送受信と、DBアクセスができるようになったから、
cakePHPで同じことをやるにはどうするんだろうと、cakePHPガイドブックを読んだ。

cakePHPには、メール送信用のコンポーネントがない。1.2で追加される予定だそうだ。
DBアクセスのところを見たら、cakePHPの方クラスをnewする必要がなく、
利用するクラスとして宣言したら、$this->クラス名として呼び出せるという違いくらい。

ZendのコンポーネントをcakePHPからも利用できるから、cakePHP+Zendという
選択肢もあるし、cakePHPを利用するなら必須になると思う。

DBアクセスのための関数は、cakePHPの方が種類が多い。
DBアクセスについては、好みの問題になるんじゃないかと思う。
私は、今のところZendのDBアクセスの方が好きだ。

機能面の充実をとるならZend、ソースの自動生成(scaffloIdやbake等)に
魅力を感じるなら、cakePHPだと思う。
scaffloIdは、かなり強力な魅力だ、公開しないページならこれで十分なときもあるだろう。
まだView部分をどちらも見ていないから、そこで差がでてくるのかもしれない。
クラス設計は、cakePHPの方がユーザがカスタムすることを前提で、空クラスが
準備されているから、どこをいじればいいのかがわかりやすい印象。
Zendでも自分で、共通クラスを生成したら、差はないと感じた。

Zendを引き続き使いながら、不便だな、便利だなと思った点がでたら、
cakePHPではどのようになっているかを調べるという方法ですすんでいこうかと思う。

Zend_DateとZend_Localeの罠

罠というほどではないのかもしれないが、今回は罠ってことにさせてもらう。
このために、3時間くらいの時間を使ってしまったから。

Zend_Dateクラスを引数なしで生成した場合、つまり
$date = new Zend_Date();
って記述した場合。

この場合は、内部でZend_Localeが呼び出され、
ブラウザ->サーバ設定->フレームワーク設定の順で、Locale設定を検索する。
Locale設定とは、日付に関する表現方法が各地で違うため、どの地域表現方法で、
表示するかを決める設定。日本語だと、1月、2月だけど、英語では、Jan、Febみたいな。
そして、現在時刻を設定したZend_Dateクラスが作成される。

今回罠に落ちたのはバッチ処理用のphpプログラムにZend_Dateコンポーネントを
利用して作成しようとして起こったのです。
バッチってことは、主にcronで呼び出され、ブラウザ経由では起動されません。
サーバ設定からLocale設定が取得できればいいのですが、試した結果、
サーバによっては、設定されていなくて、取得できない場合があるようです。
hetemlは、phpがcgi版のためか取得できてませんでした。
(ブラウザでphpのsetlocale関数の戻り値をdumpして確認しました)
フレームワーク設定は、デフォルト状態では、何も設定されていません。

Zendのソースをおったり、ネットで調べたりしてたどり着いた解決策は2つ。

メールの送信(Zend_Mail)

hetemlのメールサーバを使ってメールを送信してみた。
プログラムのサンプルはこんな感じ。

require_once('Zend/Loader.php');
Zend_Loader::loadClass('Zend_Mail');
Zend_Loader::loadClass('Zend_Mail_Transport_Smtp');
Zend_Loader::loadClass('Zend_Date');

$date = new Zend_date();

$config = array('auth'     => 'login',
       'username' => 'ユーザ名',
       'password' => 'パスワード');

$transport = new Zend_Mail_Transport_Smtp('メールサーバ名', $config);

$mailSmtp = new Zend_Mail('UTF-8');
$mailSmtp->setBodyText('本文');
$mailSmtp->setSubject('件名');
$mailSmtp->setFrom('Fromアドレス', 'From名');
$mailSmtp->addTo('Toアドレス');
$mailSmtp->addHeader('Date', $date->now()->get(Zend_Date::RFC_2822));
$mailSmtp->send($transport);

ほとんど、ドキュメントのサンプルソースを参考にしたら動いたが、ひっかかった点が以下の2点。
・日本語の文字化け
・送信日時が設定されない

日本語の文字化けは、使用している文字コードを Zend_Mailの生成時に
パラメタとして渡すことで化けなくなった。
生成時にISO-2022-JPをパラメタとし、本文と件名をmb_convert_encodingで、
ISO-2022-JPにエンコードしてから渡しても文字化けしないことも確認しました。
UTF-8としたのは、プログラムファイルの文字コードをUTF-8としているためです。

あと、なぜかわかりませんが、普通に送信したら、送信日時が空欄で届きました。
そのため、メールヘッダの Date(送信日時)を自力で設定しています。
Zend_Dateを使って、現在日時を取得し、メールヘッダに利用される
Zend_Date::RFC_2822という型に変形したものを設定しています。

メールの受信(Zend_Mail)

Zend/Mail/Storage/Pop3クラスを使って、メールを受信してみた。
受信自体は、意外と簡単に受信できたが、fromアドレスを取るのに一工夫必要。

メーラーで、メールアドレスに名前を設定されている場合、
その名前とメールアドレスの両方が、”from”情報に設定されているため、
メールアドレスだけを抜き出す必要があります。

調べてみたところ、Pearのモジュールでありました。
Mail_RFC822::parseAddressList()
という関数です。

使い方は、以下です。

require_once('Zend/Mail/Storage/Pop3.php');
require_once('Mail/RFC822.php'); 

$config = array('host'      => 'メールホスト名',
       'user'      => 'ユーザ名',
        'password'  => 'パスワード');

$mail = new Zend_Mail_Storage_Pop3($config);

foreach ($mail as $num => $message){
  $tmp = Mail_RFC822::parseAddressList($message->from);
  $mail_address = $tmp[0]->mailbox . '@' . $tmp[0]->host;
  var_dump($mail_address);
}

私が使っているサーバ heteml なら、pearにパスが通してあるので、
ZendFrameworkさえ、自分で設置すれば、pearは requireするだけで使えました。

今日まで、初期費用無料キャンペーンをやってるので、
正月休みに、いろいろ実験してみようって方は、申し込んでみてはいかかでしょう。



htaccessに苦戦した

ZendFrameworをhetemlに設置した。

.htaccessをほとんど使ったことなかったので、そこでやたら手間取ったけど、
それ以外は特につまづくこともなく、”Hello World!!”が出力された。

http://co-hey.com/xxx/に設置したことで、RewriteBaseの正しい書き方がわからず、
ネットで検索しながら、1時間くらいかかってやっと正常に表示された。

書いた .htaccess はこれだけなのに。


AddHandler php5-script .php
RewriteEngine on
RewriteBase /xxx/
RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php

http://xxx.co-hey.com というサブドメインを作成して、
http://co-hey.com/xxx/を参照するようにしたら、マニュアル通り


AddHandler php5-script .php
RewriteEngine on
RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php

でOKだった。

あー、サブドメインは設定するつもりだったから、最初から設定しておけば、
あの1時間は、他のことできていたのにーと思ったけど、これで、
.htacessを使ったmod_rewriteが多少理解できたからいいかとも思う。

さて、Zend_Mailコンポーネントの使い方を試したら、次はSmartyとの連携だ。

そういえば、使っているサーバ hetemlが2周年キャンペーンをやっている。
20日までの予定が、25日(クリスマス)まで延長されたみたい。
初期費用の4000円くらいが、無料になるからサーバ借りようかなーって人は、
これを機会に使ってみては?今のところ特に問題なく使えてますから、安心です。