「Re:デザイナーとの協業での工夫 Smartyプリフィルタの活用法」実際に行なってみたソースなど その1
昨年の11月のエントリで、デザイナーとの協業での工夫というエントリを書きました。
デザイナーとの協業での工夫 Smartyプリフィルタの活用法 - maru.cc@はてな
この案件が実際にリリースを向かえ、一通りの作業がひと段落がついたので、感想と今後行なっていこうと思っていることをまとめてみます。
また、実際に行なった仕組みのソースを公開したいと思いましたが、案件のソースは公開できません(あたりまですが)。
そこで、他のサイトを実際に作成しながら、許せる範囲で使ったものか、同等の機能を別途実装して公開したと思います。
どんなサイトにしようか迷ったのですが、長らく放置してしまっていた Ethna-users.jp がちょうどありましたので、そちらを題材に実装しようと思います。
github で公開中なので、ぜひ参考にしてください。もちろん forkしてコードにつっこみも大歓迎です。
http://github.com/marucc/ethna-users.jp/
仕組みとして作ったもの
こちらを、何回かにわけて実際の実装方法をソースをまじえて説明をしたいと思います。
1. .htmlファイルをエントリポイントにする
これは、かなり成功しました。
実は、モバイルサイトを作る場合などで実績のある仕組みなので、予想通りという感じです。サイトを作るデザイナーさんには、PHPプログラムで動くということをまったく意識せずに、aタグやimgタグの画像などに関しても、通常のhtml同士のリンクと同様に相対パスで記述できるため、マージ時に作業が発生しません。
これが、例えば、PathInfoなどを使う形式や、テンプレートファイルが .tpl などの場合に、実際に作成しているものと設置時で差異が出てしまうため、余計な手間が発生します。
テンプレートファイルを .htmlに無理やりしたとしても、画像やCSSと、テンプレートファイルを別の場所にアップする必要が出てきてしまうため、やはり余計な手間や判断が必要になってきてしまいます。
また、ほぼ静的だが、一部だけ、DBから取得したデータなどを動的に表示させたい場合に、actionは必要ないが、phpの処理が書きたいという場合にも対応をしました。
まずは、.htaccessの設定
http://github.com/marucc/ethna-users.jp/blob/e210e107e0b3a880d2aea5b33339b1b9cc8da37c/www/.htaccess
以下の部分が肝心の箇所です。
AddType application/x-httpd-php .html AddType application/x-httpd-php .htm <FilesMatch "\.html?"> php_value auto_prepend_file "./_page.php" AcceptPathInfo Off </FilesMatch>
auto_prepend_file という設定はご存知でしょうか?
PHP: コア php.ini ディレクティブに関する説明 - Manual: auto_prepend_file
PHP勉強会の懇親会などで話していて感じたたことですが、この auto_prepend_file の設定ですが、相対パスで書けるというのは意外に知られていないようです。
もちろん、相対パスで書いた弊害として、下層ディレクトリで動かなくなってしまうので、階層が違う場合にはいずれかの対応が必要になります。
- a.auto_prepend_file をフルパスで書く<今回はこれを採用しました
- b.全ての下層ディレクトリに _page.php を作る
- c.全ての下層ディレクトリに .htaccess を作り、auto_prepend_fileの相対パスを調整する
ま、常識的に考えて、フルパスでしょう。
なぜ、このような相対パスの形式の書き方をしているかというと、やはり、開発時のサーバ、ステージングサーバ、本番サーバと順に反映していく上で、フルパスが書かれてしまっているというのは、環境依存になりリリース作業の弊害になります。
.htaccessには、別の設定も記述することになるので、反映ファイルから除外しておくと、本番環境での設定漏れ等が発生してしまう可能性があります。
特に、モバイルサイトでIP制限をしている場合、.htaccessで行なうことが多いのですが、そのIP帯域追加時などに問題になったりします。
それよりは、ファイル数が少ないのであれば、全てフラットにし、多くなるとしてもディレクトリの分け方を少なくし、b,cのどちらかの対策を取る方が得策な場合もあるでしょう。
次に、auto_prepend_file で呼ばれる _page.php
http://github.com/marucc/ethna-users.jp/blob/e210e107e0b3a880d2aea5b33339b1b9cc8da37c/www/_page.php
<?php require_once dirname(__FILE__) . '/../app/Ether_Controller.php'; Ether_Controller::main('Ether_Controller', array('page'), 'notfound'); exit; ?>
Ethna特有になってしまいますが、Ether_Controller::main の第二引数を array指定にし、pageアクションのみが動くようにしているところも注意すべき点です。また、exit; も必須になります。
Ethna エントリポイント毎に実行可能なアクションを制限する
viewを判断する actionの処理
呼ばれる htmlごとに特殊な処理をしないのであれば、こちらは必要ありません。
例えば、トップページのみ最新のニュースの一覧を表示したい、といったページを作るのに、使えるかと思います。
もちろん、ニュースの一覧表示ぐらいだったら、Smartyのカスタムプラグインとして実装するのもありですけどね。ひとつの方法として参考にしてみてください。
<?php //色づけ /** * page action implementation. * * @access public * @return string forward name. */ function perform() { $base_view_name = 'page'; $action = preg_replace('!^' . $this->backend->ctl->getDirectory('www') . '/(.+)\.html?$!i', '\1', $_SERVER['SCRIPT_FILENAME']); $action = preg_replace('![^a-z0-9/]!', '', $action); $action = str_replace(array('/'),array('_'),$action); $view_name = "{$base_view_name}_{$action}"; $view_dir = $this->backend->ctl->getViewdir(); $view_path = $this->backend->ctl->getDefaultViewPath($view_name, false); if (file_exists("$view_dir$view_path")) { return $view_name; } return $base_view_name; }
コードを解説すると
- $_SERVER['SCRIPT_FILENAME'] からアクセスされたページを判別し、$this->backend->ctl->getDirectory('www') の部分のパスを取り除くことで、ファイルへのパスを判別します
- 英数字とスラッシュ以外は取り除きます これは、サイトのファイル命名規則によっては、かぶってしまい問題がでるかもしれません
- スラッシュを Ethnaのディレクトリ区切りのアンダーバーに置換します
- アクセスされたファイル専用のViewファイルが存在するか判別します もしあれば、その Viewを使用します
- 専用のViewが無い場合には、共通のpage Viewを使用します
共通の page View
実際に、アクセスされたファイルをテンプレートとしてどのように使っているかですが、以下のような指定の仕方をしています。
http://github.com/marucc/ethna-users.jp/blob/e210e107e0b3a880d2aea5b33339b1b9cc8da37c/app/view/Page.php
<?php //色づけ /** * 遷移名に対応する画面を出力する * * 特殊な画面を表示する場合を除いて特にオーバーライドする必要は無い * (preforward()のみオーバーライドすれば良い) * * @access public */ function forward() { $this->forward_path = $_SERVER['SCRIPT_FILENAME']; parent::forward(); }
forward() メソッドは、通常継承して上書きする必要はありませんが、今回は、テンプレートの forward_pathを変更するために使用します。
Ethnaでは、Viewクラスのforward_pathプロパティに入っているファイル名が、そのままSmartyに渡されるため、こちらを変更することで無理やりですがテンプレートを変更することが出来ます。
また、フルパスで指定することにより、templateディレクトリ以外のファイルを使うことも出来ます。
特定のページ用のView
特定のページ用のViewは、app/view/Page/ 以下に作成します。
例として、もし、/sample/index.html用の Viewを作成したければ、次のように作成することが出来ます。
ethna add-view page_sample_index
これらのpage以下に作成した Viewは、元となる page Viewクラスを継承する必要があります。
ページ先頭で page Viewを require_once して読み込みます。この場合に、BASEからのパスを記述している理由は、下階層のviewを作成しても、全て同一の記述をするためにこのような書き方をしています。
<?php //色づけ require_once BASE . '/app/view/Page.php';
次に、classの継承をします。
<?php //色づけ /** * page_index view implementation. * * @author {$author} * @access public * @package Ether */ class Ether_View_PageIndex extends Ether_View_Page { (略) }
今回は、直下の index.html なので、page_index がView名になります。
http://github.com/marucc/ethna-users.jp/blob/e210e107e0b3a880d2aea5b33339b1b9cc8da37c/app/view/Page/Index.php
<?php //色づけ /** * preprocess before forwarding. * * @access public */ function preforward() { // 処理いろいろ }
あとは、Ethnaの通常の流儀なので、View の preforwardメソッドには自由に処理を記述し、テンプレートとなる htmlファイルに、「ここだけはいじらないでね(はーと)」とデザイナーさんと意識あわせをして タグを書いてしまえばOKです。
もし、このpage以下全ての Viewクラスで特定の処理を行ないたい場合には、page Viewクラスの setDefaultメソッドを使用するといいでしょう。
Ethna 遷移時のデフォルトマクロを指定する。
とりあえず
この Ethna-users.jp程度ならば、ここまでの仕組みはまったく必要ないと思います。ただ、座組みやファイルを触る人が分かれる場合には、有用な場合もあるのではないでしょうか?
続きは、また書きます。