BEARで始めるWebアプリケーション開発 その6「DBを扱うリソースを作ってみよう」

前回、BEARでのDBを扱うリソースの作り方を学びました。今回は、実際にリソースを設計して実装するところまでやってみたいと思います。

まずはリソース設計から

例によって、まずはリソースの設計から入ります。

URI
/sentences
CRUD各メソッドの処理内容
"GET /sentences"で変換リクエストのあった文章のリストを出力する。"POST /sentences?s={文章}"で変換リクエストのあった文章をリストに登録する。
出力表現の形式
"GET /sentences"で変換リクエストのあった文章のリストを配列で返す。"POST /sentences?s={文章}"の場合はレスポンスのBODYは空とする。
他のリソースとのリンク関係
無し。

URIは、以前作成した"/sentence"(単数形)と紛らわしいかもしれませんが、GETした時に文章のリストが欲しいので、複数形で"/sentences"としています。*1

GETで取得できるのは、過去の変換リクエストの履歴(アクセスログ的なもの)なので、POSTで保存する時もパラメータとして文章を渡すだけで、変換結果は保存しない事とします。

ちなみに、今回のリソース設計に関しては結構悩んで、特に、変換リクエストの保存の処理をどこで行うのがよいのか、だいぶ悩んでしまいました。

考え方としては、例えば"GET /sentence?s={文章}"の処理の中で裏でこっそりDBに書き込んでしまうとか、"GET /sentence?s={文章}"の処理の中から"POST /sentences?s={文章}"を呼び出すとか、色々とやり方があると思います。

最初は、「文章の変換を行った時に履歴を保存する訳だから、"GET /sentence?s={文章}"の処理の中で保存処理しないと整合性が取れなくなる」と考えていましたが、よく考えると、変換処理と変換リクエスト保存処理が必ず同期しなければならない訳ではないので、"/sentence"とは無縁に保存処理を行うようにしました。

それにしても、リソース設計は毎回悩んでしまうんですが…リソース指向において、リソース設計こそがWebアプリケーション開発のキモになると思っているので、じっくり時間を掛けて行うべきかなと。

この辺は、色んなリソース設計を繰り返す中で、作り方のコツみたいのを掴んでいくしかないのかなと思っています。

リソースを実装する

そんな訳でリソース設計ができたので、実装に移ります。実際の作り方は前回既にやっているので、それに倣ってリソースのクラスファイルを作成するだけですね。

"App/Ro/sentences.php"ファイルを以下の内容で作成します。

<?php
class App_Ro_Sentences extends App_Ro
{
    protected $_table = 'sentences';

    public function onInject (  )
    {
        parent::onInject();
        $this->_queryConfig['pager'] = 1; // DBページャ利用
        $this->_queryConfig['perPage'] = 10; // 1ページ毎のアイテム数
    }

    /**
     * リソース読み込み
     *
     */
    public function onRead($values)
    {
        $sql = "SELECT sentence,";
        $sql.= " (SELECT MAX(created_at) FROM {$this->_table}";
        $sql.= "   WHERE sentence=a.sentence) AS created_at";
        $sql.= " FROM {$this->_table} AS a";
        $sql.= " GROUP BY sentence";
        $sql.= " ORDER BY created_at DESC";
        $result = $this->_query->select($sql, array(), $values);
        return $result;
    }

    /**
     * リソース作成
     *
     * @required s
     * @aspect App_Aspect_Transaction
     */
    public function onCreate($values)
    {
        $inputs = array(
                        'id' => md5(uniqid()),
                        'created_at' => 'NOW()',
                        'sentence' => $values['s'],
                       );
        $result = $this->_query->insert($inputs);
        if ($this->_query->isError($result)) {
            throw $this->_exception('登録できませんでした');
        }
    }
}
?>

この辺りは特に難しい事もなく、さっくりと。リソースができたところで、POSTリクエストのテストもしておきます。

$ bear create sentences?s=テスト
code
200
header
Array
(
    [_request] => create sentences?s=%E3%83%86%E3%82%B9%E3%83%88
)
body

OKですね。ちゃんと登録できたか、GETリクエストを投げて確認してみます。

$ bear read sentences
code
200
header
Array
(
    [totalItems] => 1
    [page_numbers] => Array
        (
            [current] => 1
            [total] => 1
        )

    [to] => 1
    [from] => 1
    [limit] => 1
    [_request] => read sentences
)
body
array (
  0 =>
  array (
    'sentence' => 'テスト',
    'created_at' => '2010-02-14 12:23:18.465101',
  ),
)

ちゃんと登録できました。機能を"リソース"として定義しているので、こういった単体モジュールでの機能確認がやりやすくて良いですね。

次回は?

次回は、今回作成したリソースをページから呼び出して、"最近変換した文章"として画面表示させるところまでやりたいと思います。

*1:GETした時に、複数形だとリストが、単数形だと個別の情報が取得できる、という使い分けは個人的には分かりやすいURIの付け方だと考えています。