JavaScript側にPHP変数を簡単にまるごと渡す方法 #FuelPHPAdvent2013
ハイ、昨日のオレに引き続きFuelPHP Advent Calendar 2013の6日目です。
今回の内容もまたTwig絡みです。実は昨日の記事は、本日の記事の前準備になっていたのでした。
JavaScript側にPHPのオブジェクトを渡したい
最近のWebアプリはUIのインタラクションが凝っていて、ブラウザ側のJavaScriptで色んな制御をすることも当たり前になってきました。jQueryや様々なjQueryプラグインを駆使して、ユーザに分かりやすく使いやすいサービスを提供することは、もはやウェブエンジニアとしては持っていて当然のスキルになっています。
そのようなUIを作っている際に、JavaScript側に動作パラメータの初期値を渡すのに値を一つ一つテンプレート記法で埋め込むのが面倒だったので、一発で渡せるTwig Extensionを作ったので紹介します。
data_bind関数
アイデアとしては、見えないHTML要素を作成してそのテキストに値をJSON化して突っ込もうという、まぁ普通に思いつきそうなものです。でもこれがやってみると思ったより便利で。
extension本体はこんなコードです。
<?php class Hoge_Twig_Extension extends Twig_Extension { : public function getFunctions() { return array( new Twig_SimpleFunction('data_bind', array($this, 'dataBind')), ); } public function dataBind($name, $val, $exclude = null) { if (is_object($val) && is_callable(array($val, 'to_array'))) { $val = $val->to_array(); } if (!empty($exclude)) { if (is_string($exclude)) { $exclude = array($exclude); } foreach ($exclude as $key) { unset($val[$key]); } } $fmt = '<div id="data-%s" class="hide">%s</div>'; return sprintf($fmt, $name, json_encode($val)); } }
div要素を不可視にするために、ここでは'hide'というclassを指定していますが、これはCSSで
.hide { display: none; }
的なものがあることを前提にしています。Bootstrapには含まれてますね。もちろん直接styleを書いてしまってもよいでしょう。
コントローラ側のアクションメソッドで以下のようにテンプレートに値を渡して、
<?php : function action_xxx() { : // $userinfo は情報が入ったObjectまたは連想配列 $this->template->user = $userinfo; }
テンプレート側ではこう記述します。
{{ data_bind('user', user) }}
JavaScript側でその値を使用するには、例えばjQueryだったら
var user = $.parseJSON($('#data-user').text());
と書くと、user変数にPHPで渡した値が入ります。
パラメータの解説
data_bind 関数は3つのパラメータを持ちます。
$name: 名前
HTML上で展開される名前です。'data-名前' がその要素のidになります。
$val: 変数
展開する変数です。Twigの変数になります。
$exclude: 排除するキー (省略可能)
変数を全部JS側に渡すのが楽とはいっても、ユーザ側に公開したくない内部プロパティが含まれているかもしれません。そういう場合には、第3引数にそのプロパティ名を渡すことであらかじめ削除した上で展開することができます。
単なる文字列として指定することもできますし、
{{ data_bind('user', user, 'password') }}
配列にして複数指定することもできます。
{{ data_bind('user', user, ['password', 'rank']) }}
ということでお手軽 tips でした。明日のアドベントカレンダーは@LandscapeSketchさんの[W] FuelPHP開発でローカルとWebで構造が変わっても対応できる小技 | Work Tool Smith [ワークツールスミス]です。
12/8 追記:
HTMLに埋め込む際のエスケープ処理に関しては、状況によってはjson_encode()にエスケープ用のオプションが必要です。またTwig_Function_Method はすでに古いインターフェースだと @kenji_s さんからご指摘を受けたので、現在推奨されている Twig_SimpleFunction に書き換えました。詳しくは以下をご覧ください。
FuelPHPのViewの自動エスケープについて - Blog::koyhoge
FuelPHPでTwig Extension #FuelPHPAdvent2013
4日目の@mkkn_infoさんのFuelphpのエラーハンドリングがなんか今ひとつ物足りなかったのでなんとかしてみた話 - どうにもならない日々@mkknに引き続き、FuelPHP Advent Calendar 2013の5日目です。
ここ数年はアドベントカレンダーの時にしか技術的な内容を書いていない気がするのが恐ろしいところですが、気にせずいきましょう。
FuelPHPのParserパッケージ
FuelPHPは、基本的にはビューに生のPHPスクリプトを使うことになっていますが、標準バンドルされているParserパッケージを用いることで、様々なテンプレートエンジンを用いることができます。現在サポートされているエンジンは以下の通り。
このうち自分ではTwigを愛用しています。何か機能を追加するにも簡単にできるところが良いですね。
Parserパッケージが標準で用意してくれるFuelPHP向けExtension
ParserパッケージでTwigを使用すると、Uri, Config, Form, Input, Html, Asset などの便利そうなFuel coreのメソッドを、あらかじめTwig Extensionとしてロードしてくれます。これを行っているのは
fuel/packages/parser/classes/twig/fuel/extension.php
にある\Parser\Twig_Fuel_Extensionクラスで、これ自体も標準的なTwig Extensionです。
これのおかげで、例えば
Asset::js('hogehoge.js');
を呼びたい場所では
{{ asset_js('hogehoge.js') }}
と書くことができるわけです。
アプリ独自のTwig Extensionを使う
とはいえ、ただ単にTwigを使ってHTMLテンプレートを書くだけではなく、アプリケーション独自のTwig Extensionをがしがし登録して使いこなしてこそTwigの便利さが際立つというもの。早速やってみましょう。
独自のTwig Extensionを登録するには、まずTwig_Extensionクラスを継承したクラスを作ります。クラス名は他とぶつからなければ何でも良いですが、ここではHogeアプリ向けにHoge_Twig_Extensionという名前にすることにしましょう。FuelPHPのファイル名規則に則り以下の場所に作ります。
中身はこんな感じ。
<?php class Hoge_Twig_Extension extends Twig_Extension { /** * Gets the name of the extension. * * @return string */ public function getName() { return 'hoge'; } /** * Sets up all of the functions this extension makes available. * * @return array */ public function getFunctions() { return array( new Twig_SimpleFunction('swap_empty', array($this, 'swapEmpty')), ); } /** * Sets up all of the filters this extension makes available. * * @return array */ public function getFilters() { return array( new Twig_SimpleFilter('json', 'json_encode'), ); } public function swapEmpty($value) { return empty($value)? '-' : $value; } }
ここではTwigの関数とフィルターを一つずつ登録しています。
テンプレート上では以下のように使います。
{# 変数の設定、本来はPHP側から渡される #} {% set foo = 0 %} {% set bar = {'fuga': 'hoge', 'move': 'puge'} %} {{ swap_empty(foo) }} {{ bar|json }}
ただファイルを置いただけではParserパッケージはそのExtensionの存在を知らないので、Parserのconfigを通して教えてやります。FuelPHPのConfigは大変に賢くて、追加・変更したい部分だけapp以下に書けば良いので、
fuel/app/config/parser.php
に以下の内容を記述します。
<?php return array( 'View_Twig' => array( 'extensions' => array( 'Hoge_Twig_Extension', ), ), );
Twigは非常に柔軟性の高いテンプレートエンジンで、上記で紹介した関数・フィルターの他にも
- グローバル変数
- タグ
- オペレータ
- テスト
などを独自に拡張できます。詳しく知りたい方は、Extending Twig を読むとよいでしょう。
明日のアドベントカレンダーも引き続き私ですw
12/8追記:
Twig_Function_Method, Twig_Filter_Function はすでに古いインターフェースだと @kenji_s さんからご指摘を受けたので、現在推奨されている Twig_SimpleFunction, Twig_SimpleFilter に書き換えました。
「PHPエンジニア養成読本」が9月13日に出版されます
来月9月13日(金)に技術評論社より「PHPエンジニア養成読本」というムックが発売されます。新原さんのエントリや増永さんのエントリがすでにホッテントリ入りしているので、もうご存知の方も多いかもしれません。大きく変わりつつある PHP 開発のイマドキの常識を、可能な限りピックアップした本です。
内容についてはすでに上記の2エントリで的確に解説されていますので、ここでは視点を変えて、きっかけを作った一人としてこのムックが生まれた背景などを書いておこうと思います。
企画の相談が来たのは4月中旬
私は以前にWEB+DB PRESSで「PHPこども電話相談室」というふざけたタイトルの連載をしていました。その時からお世話になっている技評の編集者の細谷さんから、「PHPエンジニア養成読本」というムックの企画があるので相談に乗ってもらえないかというメールが来たのは4月中旬でした。PHP を取り巻く開発環境が大きく変わりつつある中で、それらの情報がまとまっている出版物はほぼ無いという状況で、たぶん需要もあるだろうし出版する価値も大きいと思った私は、二つ返事で OK しました。
その後、わりかしのんびりとしたメールのやりとりでネタ出し等をおこなったあと、5月22日に細谷さんとお会いして打合せをしました。その場では色んな話をしましたが、今回のムックの出版日ターゲットとして、その時にはすでに開催日が決まっていた PHPカンファレンス2013 に合わせる形にしたい、という要望を聞きました。それを聞いた時に私は「そりゃ無理ですよー」と返事をしたのを覚えています。その時に自分が想定していた執筆者候補は、たいてい PHPカンファレンスのスタッフになっているので、カンファレンス前のテンパっている時期に執筆時記を重ねるのはリスク高え、と思ってました。
PHPカンファレンス関西の飲み会で新原さんに相談
その打合せの直後にPHPカンファレンス関西2013が開催されました。私は毎年参加しているので今回も参加したわけですが、カンファレンス前日の宴会で「このムックの執筆陣を関西 PHP コミュニティで確保できないか?」という相談を新原さんにしてみました。新原さんも幸い興味を持ってくれて、ここから一気に企画が具体化し始めます。
執筆者の Facebook グループが作られて、そこで企画内容を揉んだり、執筆原稿は github で管理することが決まったり、様々な物事がどんどん詰められていきました。github での原稿を共同執筆という経験は自分は初めてで、他の執筆陣から徐々に上がってくる原稿を見ながら、高まるプレッシャーを感じていました。
自分はというと、別口の原稿依頼がもう一本あってそちらに時間が取られたり、ちょっとサボり癖がでたりして、最初の〆切を大幅にオーバーしてからエンジンが回り始めて一気に書き上げました。ちなみにその別口の原稿というのが、@IT で公開された以下の記事です。
Symfony, FuelPHP の紹介記事を3ページずつ書きました
さてそういうわけで、他の方々に心配をかけながらも、無事に原稿は書き上がりました。Symfony と FuelPHP の紹介記事をそれぞれ 3 ページずつ執筆しました。
MVC がどうなっているとか、基本的なことはどのフレームワークも大して変わらないので、自分なりにそれぞれのフレームワークを使ってみて感じた、そのフレームワークならではの特徴を抽出して執筆したつもりです。その意味では、自分のパートは入門者には実用性は全くありませんが、そのフレームワークのキモの考え方をパッと知りたいというニーズには合ってるんじゃないかと思います。
PHPカンファレンス2013の会場で是非ご購入下さいw
PHPカンファレンス2013は、今年もWordCamp Tokyo 2013と共催で、大田区蒲田産業会館 PiO にて、9月14日(土) に開催されます。そうです、「PHPエンジニア養成読本」の発売日の翌日です。PHPCon には今年もジュンク堂さんが出展して書籍販売します。当然、本ムックも販売リストの中に入っています。会場内には私も含め執筆陣も多数いるはずですので、サインを集めるには最適かもしれません。
fuelphp-dynamoutilを公開した
さて、FuelPHP 勉強会 東京 vol.3 : ATND で発表したスライドの中で、FuelPHP で Amazon DynamoDB を使いやすくする SDK のラッパーを OSS で公開したいと予告していたのですが、先ほど github で公開しました。
ドキュメントはまだないです。クラス 4 つしかない小さなものなので、ソース読んだだけでも使い方は分かるかもしれません。
FuelPHP勉強会東京 vol.3に参加してきた
月の最後の日をPHPで簡単に知る方法
集計系の処理のプログラムを開発していると、その月の最後の日を求める必要がちょくちょくあったりしませんか? 私はあります。さてそういう時にどうやって求めるでしょう。答えは一発↓
<?php $d = date('Y-m-t');
これだけですw
書式「t」って何ですか?
恥ずかしながら date() 関数の書式に使える「t」の存在を今日まで知りませんでした。意味としては以下になっています。
t : 指定した月の日数。
t は DateTime クラスにも使えます。
例えば今年の各月に対して最終日を求めるのには、以下のようにします。
<?php $d = new DateTime(); $year = 2012; foreach (range(1, 12) as $month) { $d->setDate($year, $month, 1); echo $d->format('Y-m-t') . "\n"; }
従来はどうやっていた?
t フォーマットを知る前の従来のセオリーはこうでした。
- その月の最初の日(1日)を求める
- それに "+1 month" する
- それにさらに "-1 day" する
コードで書くとこんな感じ。
<?php $d = new DateTime(); $d->setDate($d->format('Y'), $d->format('m'), 1); $d->modify('+1 month -1 day'); echo $d->format('Y-m-d');