Stellaqua - TOMの技術日記 このページをアンテナに追加 RSSフィード

2013年07月31日

[][][][]BEAR.Sunday meetup #2に行ってきました

何と、前回の記事の日付が2年前という恐ろしい状況!…止まった時計を再び動かすべく、はてなダイアリーに戻って参りました。

という事で、BEAR.Sunday meetup #2にお邪魔させて頂いてきたので、ご報告を。

会場はとってもオシャレなHubTokyoさん。WebSterさんの提供で、食べ物・飲み物に困らずに非常にリラックスした状況で、なるほど、これが勉強会ではなくmeetupなんだと実感しました。

全員が自己紹介も兼ねてLT必須という事で、何の話をしようかと思ったんですが、ちょうどTDDフレームワークのWaltzを作っていて、DocTestが動く状態になったところだったので、その紹介をさせて頂きました。

http://www.slideshare.net/stellaqua/bea-rmeetup2-lt

他の方が時間節約の為に1台のPCに資料をまとめてプレゼンしている中、どうしてもDemoをやりたい、というわがままで自分のPCでLTさせて頂きました。頑張ってビジュアルにこだわったテスト結果の楽譜表示は、特に女性陣には好評だったようで、満足しとりますw Waltzのもっと深い話は、またいつかどこかで…。

自分のLTはともかくとして、みなさん3分間という短い時間なのに(自分も含めてオーバーしてた人多数ではありましたがw)、とっても濃い内容で素敵でした。

また、メインセッションの方も、@koriymさん、@mackstarさん、果ては、NateさんのAngularJSの話が直に聞けるという何とも贅沢なmeetupでございました。特に、@koriymさんのプレゼンは、「いよいよBEARの世界進出が始まる…!?」と思いながら、本当にワクワクしながら聞いていました。

自分もBEARを応援し続けてきた一人として、このワクワク感をもっと世に広めるべく、自分にできる事を進めていこうと心新たにしたところで、筆を置きたいと思います。

2010年04月25日

[][][][]BEARで始めるWebアプリケーション開発 その12「Ajaxアクセスを試してみる」

現在、今まで作ったWebサービス達をBEARベースに移行しようとしていまして、どうせならAjaxも使いたいなと思って、BEARでのAjax周りの学習をしていました。そんな訳で、今回はBEARでのAjaxアクセスに関するお話です。

今回は例として、Googleの検索APIを叩いて検索結果を返す処理をAjaxで実現するアプリケーションを作ってみたいと思います。*1

事前準備

BEARではJavaScript周りはjQueryベースのパッケージスクリプトが用意されていて、Ajaxサーバ側のBEARアプリケーションとデータをやり取りする事ができるようになっています。

init-appした直後はその辺りのJavaScriptファイルは用意されないので、PEARインストールディレクトリからコピペして用意します。

$ mkdir $BEAR_DIR/htdocs/js
$ cp -ir $PEAR_DIR/BEAR/data/htdocs/bear $BEAR_DIR/htdocs/js/

$PEAR_DIRはPEARインストールディレクトリ、$BEAR_DIRはinit-appした時のディレクトリ*2です。コピー先はhtdocsの下ならどこでもいいんですが、今回は、"htdocs/js/bear"というディレクトリの下に置くようにしました。

テンプレートを作成する

テンプレートから作成すると見通しが良いと思うので、まずはテンプレートから作成します。"App/views/pages/ajaxindex.tpl"というファイルを以下のような内容で作成します。

<html>
<head>
<script type="text/javascript" src="/js/bear/jquery.bear.min.js?{appinfo version}"></script>
<script type="text/javascript" src="/js/app.js?{appinfo version}"></script>
</head>

<body>
<h2>Ajaxテスト</h2>
<form id="ajaxform">
    <input type="text" name="keyword" size="20">
</form>
{a rel="ajax" href="./ajax.php" click="post"}検索{/a}
<dl>
    <dt>結果</dt>
    <dd id="response"></dd>
    <dd id="result"></dd>
</dl>
</body>
</html>

ポイントの一つ目は、"jquery.bear.min.js"というファイルを読み込んでいる点ですね。このファイルには、jQuery本体とBEARのJavaScript機能がパッケージされているので、Ajax処理をするには、何はなくともこのファイルが必須です。

次に、"app.js"というファイルを読み込んでいますが、これについては後述します。

続いてのポイントは、"{a}"というBEARの独自タグです。これは、HTMLのaタグの上位互換性を持っていて、いくつか拡張機能が使えるようになっています。特にclick属性は、JavaScriptの"onClick"イベントのような感覚でサーバアプリの処理を呼び出せる感じで、なかなか面白い機能だと思います。*3

ここでは、「Ajaxアクセスで、"ajax.php"の"post"クリックハンドラを呼び出す」というリンクを定義しています。

後は、Ajaxアクセスの結果を格納するボックスをIDを付与して置いているだけですね。

このテンプレートに対するページファイルは、単純に表示するだけのものなので、以下のように中身が何も無いクラスファイルを作っておけばオッケーですね。

<?php
require_once 'App.php';

class Page_Ajaxindex extends App_Page
{
}
App_Main::run('Page_Ajaxindex');
?>

これを、"App/htdocs/ajaxindex.php"として作成しておきます。

Ajaxで呼び出されるページファイルを作成する

次に、Ajaxアクセスで呼び出されるページファイルを作成します。"htdocs/ajax.php"というファイルを以下の内容で作成します。

<?php
require_once 'App.php';

class Page_Ajax extends App_Page
{
    public function onInject()
        $this->_ajax = BEAR::dependency('BEAR_Page_Ajax');
        $this->injectAjaxRequest();
    }

    public function onClickPost ( $args )
    {
        $keyword = $args['form']['ajaxform']['keyword'];
        $result = $this->_resource->read($params)->getBody();
        if ( is_null($responses = json_decode($result[0], true)) === false ) {
            if ( $responses['responseStatus'] === 200 ) {
                $response = 'OK';
            } else {
                $response = $responses['responseDetails'];
            }
        } else {
            $response = 'API取得エラー';
        }
        $this->_ajax->addAjax('html', array('response' => $response));
        $this->_ajax->addAjax('js', array('display' => $result[0]));
    }

    public function onOutput()
    {
        $this->output('ajax');
    }
}
App_Main::run('Page_Ajax');
?>

ここはポイントがたくさんですね。順を追って見ていきたいと思います。

まず、onInject()でBEARのAjaxコンポーネントの準備と、Ajaxアクセスのリクエストパラメータ受け取りの処理をしています。

次に、onClickPost()で、テンプレートで書いた{a}タグがクリックされた時の処理を書いています。Ajaxアクセスした時のパラメータは$argsに自動的に格納されて、フォームの内容は$args['form']で受け取る事ができます。フォームに付けたID毎に連想配列として格納されるので、複数のフォームを作ってもちゃんと値を区別して受け取る事ができます。

受け取ったキーワードを使ってGoogle検索APIにアクセスした結果の内、処理結果を$response、検索結果を$resultに入れています。

この結果を、addAjax()というメソッドで返していますが、"html"と"js"という2種類の方法で返すようにしています。*4

"html"で返した場合は、HTMLの中の該当するIDの場所にBEARが自動的に挿入してくれます。また、"js"の場合は、引数として値を渡しつつ、自分で定義したJavaScriptメソッドを呼び出す事ができます。今回の場合は、Google検索APIの処理結果のJSON引数にして、"display"というメソッドを呼び出すようにしています。

アプリケーション独自のJavaScriptを定義する

続いて、Ajax呼び出し後に結果を格納したりする処理を作成します。"htdocs/js/app.js"というファイルを以下の内容で作成します。

(function($) {
    $.app = {
        display : function(result) {
            eval('var results=' + result);
            var html = '<ul>';
            $.each(results.responseData.results, function(i, item){
                html += '<li>' + item.titleNoFormatting + '</li>';
            });
            html += '</ul>';
            $("#result").html(html).show('slow');
        }
    }
})($);

$(document).ready( function() {
    // rel=ajaxとなっているAタグをAjaxLink化する。
    $("a[rel^='ajax']").bearAjaxLink({form:true});
});

一つ目のブロックがクリックハンドラ側から呼び出されるメソッドの定義で、JSONをevalして、HTML内に埋め込む処理をしています。

二つ目のブロックはHTMLの読み込み後の初期化処理で、BEARのパッケージの機能を使って、rel属性が"ajax"となっているaタグに対してAjax処理を行うイベントを設定しています。引数で"{form:true}"としておくと、クリックハンドラを呼び出す時に、自動的にHTML内のフォームの内容をパラメータとして渡すようになります。

Ajaxアプリケーションの完成!

ここまでで、HTMLからのAjaxでの値の送信、サーバでの処理結果の受け取りと表示、という基本的な部分について作成できました。

f:id:stellaqua:20100425202524p:image

キャプチャした時には、init-appした時に作成されるデフォルトCSSを適用したものを使ったので、今回の記事のテンプレートファイルだとこの画像の通りにはなりませんが、まぁイメージ映像という事で…。

ちなみに、画像だと伝わりませんが、結果を表示する時にjQueryのエフェクトを入れているので、ボワッという感じで表示されます。jQuery使うと、ちょっとしたエフェクトが簡単に入れられてなかなか良いですな。

雑感

という訳で、AjaxアプリケーションをBEARで作ってみましたが、「参考までに」と思ってCakePHPAjaxヘルパーの使い方をちょっとだけググって見てみたら、基本的なやり方は同じような感じっぽい印象でした。

ただBEARの場合、「aタグにイベントを定義して、そのクリックハンドラを"サーバ側の"ページに定義する」というメタファーになっているところが個人的に気にいっていたりします。

次回は?

次回は、テンプレート周りの話にしようかなという予定です。

*1:実際には、Google検索APIは直接JavaScriptから呼び出して使った方が断然便利なので、わざわざAjaxアクセスしてサーバ側でAPI叩くなんてのは全くスマートとは言えませんが、まぁ、サンプルとしてこんなもんで…。

*2:"bear show-app"で確認する事ができます。

*3:{a}タグの詳細は、本家Wikiクリックハンドラのページ参照。

*4:もう一つ"val"というのもありますが、今回は省略。

2010年04月16日

[][][][]BEARで始めるWebアプリケーション開発 その11「リソースをWebAPIとして外部に公開する」

今回は、リソースをWebAPI*1として外部に公開する為の方法について学びます。

リソースをWebAPIとして提供する場合、外部からはGETのみで、かつ返す表現がXML・JSONの場合は非常に簡単で、PageのonOutputハンドラでdisplayメソッドを使っていたところを、outputメソッドに変えるだけです。

<?php
    public function onOutput()
    {
        $this->output('xml');
        //$this->output('json');
    }
?>

BEARでは、リソースが値を返す時点では、表現を伴わない単なるデータ構造になっているので*2、状況に応じた表現の変更を極めて容易に行う事ができます。

この、表現形式を決めるアウトプットフィルタは自分で作る事もできて、"App/Resource/output"というディレクトリを作って、その中に"{形式名}.php"というファイル名でPHPファイルを作成すると、それを利用する事ができるようになります。

デフォルトで用意されているアウトプットフィルタは、PEARディレクトリ配下の"BEAR/Resource/output"の中にあります。デフォルトのアウトプットフィルタの動作を変えたい場合は、ここから"App/Resource/output"にコピーしてきて中身を書き換えてやれば、そちらが優先して使われるようになります。

今回は試しに、何らかの画像生成を行ってIMAGICKオブジェクトを返すリソースを作った時に、それを画像ファイルとしてHTTP出力するアウトプットフィルタを作ってみました。

<?php
/**
 * イメージ出力
 *
 * @param array $values  出力データ
 * @param array $options オプション(type:gif|jpeg|jpg|png , expire:有効期限)
 *
 * @return BEAR_Ro
 */
function outputImage($values, array $options)
{
    $ro = BEAR::factory('BEAR_Ro');
    $type = ( isset($options['type']) === true ) ? $options['type'] : 'png';
    switch ( $type ) {
        case 'gif':
            $mime = 'image/gif';
            break;
        case 'jpeg':
        case 'jpg':
            $mime = 'image/jpeg';
            break;
        case 'png':
            $mime = 'image/png';
            break;
    }
    $expire = ( isset($options['expire']) === true && $options['expire'] > 0 )
        ? $options['expire'] : 0;
    $headers = array();
    $headers['X-BEAR-Output: IMAGE'] = 'Content-Type: '.$mime;
    $headers[] = 'Expires: '.$expire;
    $headers[] = 'Last-Modified: '.gmdate('D, d M Y H:i:s ', time()).' GMT';
    $headers[] = 'Cache-Control: public';
    $headers[] = 'Pragma: ';
    $ro->setHeaders($headers);
    $ro->setBody(array_shift($values));
    return $ro;
}
?>

これを、"App/Resource/output/image.php"として作成しておいて、Pageファイルのアウトプットフィルタを以下のようにすると画像データを返すようになるので、imgタグのsrcとかで使えるようになります。

<?php
    public function onOutput()
    {
        $this->output('image', array('type' => 'png'));
    }
?>

今回は、リソース側で画像生成するような場合の想定でしたが、「リソース自体は普通に連想配列を返すけど、出力する時にその情報を利用して画像生成してHTTP出力する」*3なんていうアウトプットフィルタも簡単に作れる事が分かるかと思います。

次回は?

今回は実は元々、外部からリソースのCRUD操作を行えるような、RESTfulWebAPI用Page作成にしようと思っていたんですが、現状、PUTやDELETEも含めた外部向けWebAPIを作る予定は無かったし、むしろアウトプットフィルタの方が使いどころが多いかなと思って、アウトプットフィルタの話にしました。

次回の内容はまだ決めていないのですが、イベント駆動の辺りかAjaxか、そんな辺りの話にしようと思います。

*1:予告では"RESTfulWebサービス"という言い方をしましたが、"Webを支える技術"での使い分けに倣って、"WebAPI"としました。

*2:"リソース自体は表現を決めない"という特徴がある故に、リソース自体で考えると対人用とか対機械用とかの区別が無いモノと言えるので、"Webを支える技術"で言うところの「WebサービスとWeb APIを分けて考えない」という事を実現できているのではないかと思います。

*3:具体例で言うと、はてブ数APIみたいな感じで、リソースは数値データだけを返すようにしておいて、アウトプットフィルタで画像生成するようにするイメージ。

2010年03月23日

[][][][]BEARで始めるWebアプリケーション開発 その10「アスペクト指向プログラミングしてみる」

今回は、BEARの特徴の内の一つである"アスペクト指向"について学びます。概要の説明は例によって本家Wikiに譲ります。

Google Code Archive - Long-term storage for Google Code Project Hosting.

アスペクト指向してみる

BEARの場合AOPは、phpdoc形式のコメントに記述する、アノテーションにより実現されていますね。

例えば、DBアクセスのあるリソースでトランザクションがかかるようにしたい場合、該当のメソッドで以下のようにコメントに書いておくだけという、何ともお手軽な感じですね。

<?php
class App_Ro_Entry extends App_Ro
{
    /**
     * リソース作成
     *
     * @aspect around App_Aspect_Transaction
     */
    public function onCreate($values)
    {
        $values['created_at'] = _BEAR_DATETIME; //現在時刻
        $result = $this->_query->insert($values);
        if ($this->_query->isError($result)) {
            throw $this->_exception('投稿できませんでした');
        }
    }
}
?>

Transactionアドバイスに関してはinit-appした時に自動的に作成されるので、わざわざ自分でアドバイスを作成する必要もなく、トランザクション処理を簡単に行う事ができます。

AOPはうまく利用すると、コードを書く量を劇的に減らす事ができますね。

アドバイスの実行順序

アドバイスは一つのメソッドに複数指定した場合、before→around→afterの順で実行され、beforeやafterが複数ある場合は、before・after毎に指定した順で実行されるようです。

<?php
/**
  * @aspect after App_Aspect_After1
  * @aspect before App_Aspect_Before2
  * @aspect after App_Aspect_After2
  * @aspect around App_Aspect_Around1
  * @aspect before App_Aspect_Before1
  * @aspect around App_Aspect_Around2
  */
?>

こんな書き方をすると、"Before2→Before1→Around1→After1→After2"という順で実行されます。

aroundに関しては複数記述した場合は、最初に記述したものだけが実行される仕様のようです。(上記例の場合、Around2は実行されない。)

アノテーション

コメント部分に書く事ができるアノテーションは、今のところAOPの@aspectと、メソッドの必須項目を定義できる@requiredが使えます。

とりあえずは@requiredだけあれば困らなさそうですが、リソースに対する入力値の事前チェックなんかをやりたいと思ったら、@aspectでチェック用のアドバイスを作っちゃえば良さそうですね。

という訳で、AOPに関してはさらっと見てみただけですが、実際には周到に最初から準備するというよりは、リソースをいくつも作っていく内に、よく出てくる処理をアドバイスとして切り出す…というような形で使っていくのが良さそうな感じがしました。

また、アノテーション機構は、実際のコードの近くに定義が来るので、コードの見通しが良くなってよいですね。DocTestも併用すれば、テストもコメント部分に書けて、そのメソッドに関する情報が一箇所に集まる感じで非常に素晴らしいですね。

次回は?

次回は、BEARでのRESTfulWebサービスの提供の方法について、またはその前段としてDIに関して書こうかなと思っています。

2010年03月10日

[][][][]BEARで始めるWebアプリケーション開発 その9「フォーム関連機能を学んでみる」

今回はBEARでのフォーム関連の機能を学習します。

…とは言ってもBEARの場合、フォーム関連はPEAR::QuickFormのラッパー的な感じなので、フォームの構築方法自体はQuickFormの方を学ぶ必要がありそうです。なので、QuickFormの細かい使い方はひとまず置いておいて、今回はBEARでのフォームの使い方を学んでいく事にしたいと思います。

フォーム構築の方法

基本的な使い方は本家Wikiデモページを見ると非常によく分かるので、そちらを参照しながらフォーム構築方法を見ていきましょう。

フォームは、一つのまとまった単位(formタグで括られる範囲)毎にクラスファイルを作成して、その中でそのフォームに関する設定・処理を全て書いていくというスタイルが推奨されるようです。

という事で、よくある"入力→確認→完了"という画面遷移の場合のフォームクラスのサンプルを見ていきます。

http://code.google.com/p/bear-demo/source/browse/trunk/App/Form/Preview.php

基本的な流れとしては、フォームの状況(初期状態/確認画面/入力修正)に応じたonInjectメソッドを用意しておいて、そこで各状況毎の設定を行って、実際のフォーム部品の内容は、buildメソッドで定義する感じですね。

バリデーションルールもこのクラスの中で定義するので、このフォームに関する設定の全てが一つのクラスファイルに集まっていて、非常に見通しが良くて素晴らしいですね。

続いて、このフォームを利用するページファイルの方も見てみます。

http://code.google.com/p/bear-demo/source/browse/trunk/htdocs/form/preview/index.php

各メソッドの処理は、

onInjectメソッド
フォームの状況切り替え
onInitメソッド
DI経由でフォームクラスのbuildメソッド呼び出し
onActionメソッド
実際にsubmitボタンが押された場合の処理

という感じですね。

BEARは処理の役割分担が明確なので、一つのメソッドにダラダラ書いていく感じにならなくて、スッキリ書けて良いですね。

これだけで、Smartyテンプレート内で"{$form}"とだけ書けば、入力画面・確認画面の区別無く、必要な情報が全てレンダリングされた状態でフォームを表示する事ができます。

セキュリティに関しても、CSRFの防止や二重投稿の防止も自動的に考慮されるようになるので、難しい事を何も考えなくてもフォームでの値入力UIが作成できちゃいますね。

フォーム関連で、特に"入力→確認→完了"の画面遷移を伴う処理はとかく面倒な事が多いですが、BEARでは非常に簡単にフォーム処理ができる事が分かりました。

とは言え"簡単"という事は、QuickFormが自動的に処理している部分が多い訳で、QuickFormのデフォルトに乗っかる事ができる部分が大きければ非常に便利に使えますが、カスタマイズが必要な部分が多かったり、極めて単純なフォームの場合は、むしろフォームは自前で用意するようにした方がよいのかもしれないですね。

とりあえず、今のところQuickFormを使った方がよい程のフォームは扱わないので、今回はBEARでのフォームの扱い方をざっくり学んだ…というところまでにしておこうと思います。

次回は?

次回はアノテーション・アスペクト指向の辺りを学習しようと思います。