2012年7月28日土曜日

BEAR.Sundayでコンタクトフォームでも作ってみる

BEAR.Sundayをインストールしたので、
簡単なコンタクトフォームでも作ってみることにした。
何しろtwigすら使ったこと無いので、何もかもが手作業・・・

そして、コードが見にくいですね。すいません。
bloggerがmarkdownに対応してほしい。

Contact.phpを作る



sandbox/Resource/Page/Contact.phpとして以下を作成。

<?phpnamespace sandbox\Resource\Page;
use BEAR\Framework\Resource\AbstractPage as Page;
class Contact extends Page{
    public $body = [];
    public function onGet()
    {
        $this->body[ 'message' ] = 'Please fill in the form.';
        return $this;
    }
}

これが「リソースオブジェクト」になる。
namespaceとファイルの場所を一致させる。違うと何かエラーが出た。
ちなみに、これだけだと何も表示されない。
内容は$this->bodyに入る。そしてonGetメソードの最後のreturn $thisで、内容とともに全ての情報をフレームワークに返している。


テンプレートファイルを作成


sandbox/Resource/Page/Contact.tplとして以下を作成。


<!DOCTYPE html>
<html lang="en">
<body>
<h1>Contact Me</h1>

<p>{$message}</p>
</body>
</html>
アクセスすると「Contact Me」と「Please fill in the form」と表示された。

リソースオブジェクトのクラス名からテンプレートファイルを決定(言うなれば拡張子をtplに変えてる)、テンプレートファイルから表示しているようです。

contact  フォームを作る


次はコンタクトフォームを作ってみる。
以下のformをbody内に書き込んでみた。

<form action="/contact" method="POST">
    <input name="X-HTTP-Method-Override" type="hidden" value="POST" />
    <div class="control-group">
        <div class="controls">
            <input type="text" id="title" name="title" >
            <p class="help-inline"></p>
        </div>
        <div class="controls">
            <textarea type="text" id="body" name="body" rows="8"></textarea>
            <p class="help-inline"></p>
        </div>
    </div>
    <input type="submit" value="Submit">
</form>
大事なのは隠しタグでHTTPメソードをPOSTにしてるところ。
SubmitするとonPostメソードが無いと怒られるので、次のメソードをContact.phpに追加してみた。

    public function onPost()
    {
        $this->body[ 'message' ] = 'You have tried to contact me.';
        return $this;
    }
まさにHTTPメソードがオブジェクトのメソード名に一対一に対応しているのがよくわかります。アクセスすると、メッセージが「コンタクトしてくれたね」みたいな感じになったので動いてますね。結構、簡単です。

フォームの内容を受け取る


onPostの引数に「$title」と「$body」を追加。
    public function onPost( $title, $body )
すると、フォームの内容を受け取れました。

簡単すぎる。
どうやって動いてるのだろう?

本来なら、フォームを受け取ったら、データをモデルに渡して処理を行ってもらうのが正しいと思います。Blogのサンプルコードを見ると、次のようにして処理を受け渡せるようです。
$this->resource->post
        ->uri('app://self/blog/contact')
        ->withQuery(['title' => $title, 'body' => $body])
        ->eager->request();
この場合、Resource/App/Contact.phpクラスを作り、その中で実際の処理を行うことになります。

今回はサンプルですし、メールを出すだけなので、ここではPageのクラス内で処理してしまいます。mail_jp( $mailto, $title, $body );とか書くだけですし。(サンプルなので実際にメールを送信はしません。)

Page内で処理を行うべきか?
行うべきではないですが、まぁいいか、と思う場合はありますよね?

エラー処理をPageとTemplateに加える


フォームと言えばエラー処理。
必須項目の処理を加えました。
エラー表示も他のテンプレートを参考にして修正。

結果:

sandbox/Resource/Page/Contact.php:
<?php
namespace sandbox\Resource\Page;
use BEAR\Framework\Resource\AbstractPage as Page;
class Contact extends Page
{
    public $body = [
        'title' => '',
        'body'  => '',
        'error' => [ 'title' => '', 'body' => '' ],
    ];
    /**
     * @return Contact
     */
    public function onGet()
    {
        $this->body[ 'message' ] = 'Please fill in the contact form.';
        return $this;
    }
    /**
     * @param $title
     * @param $body
     * @return Contact
     */
    public function onPost( $title, $body )
    {
        $this->body[ 'message' ] = 'You have tried to contact me.';
        $this->body[ 'title' ]   = $title;
        $this->body[ 'body' ]    = $body;
     
        if( !$title ) $this->body[ 'error' ][ 'title' ] = 'missing title';
        if( !$body  ) $this->body[ 'error' ][ 'body'  ] = 'missing message body';
     
        if( $title && $body ) {
            // mail_jp( $mail_to, $title, $body );
        }
        return $this;
    }
}


sandbox/Resource/Page/Contact.tpl:

<!DOCTYPE html>
<html lang="en">
<body>
<h1>Contact Me</h1>
<p>{$message}</p>
<form action="/contact" method="POST">
    <input name="X-HTTP-Method-Override" type="hidden" value="POST" />
    <div class="control-group {if $error.title}error{/if}">
        <div class="controls">
            <input type="text" id="title" name="title" value="{$title}">
            <p class="help-inline">{$error.title}</p>
        </div>
    </div>
    <div class="control-group {if $error.body}error{/if}">
        <div class="controls">
            <textarea type="text" id="body" name="body" rows="8">{$body}</textarea>
            <p class="help-inline">{$error.body}</p>
        </div>
    </div>
    <input type="submit" value="Submit">
</form>
</body>
</html>

これでエラー処理が出来ました。


もっとも、メール送信が出来た場合の処理が不十分です。

送信できた場合、今のままだと同じテンプレートを読み込んでしまいます。新たにコントロール変数を追加して、表示内容を変えてもいいのですが、間違った対応方法な気がします。

別に「送信完了、ありがとう」ページを作って、そこに飛ばすのが一番簡単でしょう。

あるいはPageオブジェクト内で、処理の結果により読み込むテンプレートを変更できるとよいのですが。今のところ可能なのかどうか、どうやるのか、さらにはそれが正しい処理なのか、分かりません。

2 件のコメント:

koriym さんのコメント...

>>Pageオブジェクト内で、理の結果により読み込むテンプレートを変更

リソースのリクエストメソッドでは表示に関して関心を払いません。自身の状態だけを考えます。

>>新たにコントロール変数を追加して、表示内容を変えてもいいのですが、間違った対応方法な気がします

間違ってないです。状態に応じてテンプレート側で読み込むファイルを変える等、問題ありません。

リソースは状態だけに関心を持ち、表現はテンプレートやレンダラーが責任をもちます。例えばこのページリソースをAPIとして考えたり、テストをするときの事を考えると分かりやすいのではないでしょうか。

Asao Kamei さんのコメント...

回答ありがとうございました。
なるほど、リソース自身の状態が問題なのであって、表示に関しては何でもいいわけですね。