チャット作るよ。9日目

超放置してた。やっぱりなかなか継続できないなー。
今日は表示系のアクションとテンプレ周り。
テンプレートは最初に作ったHTMLのモックに手をいれた。

メインページを作る。

まずはメインページ。indexページ的なもの。
基本的にこのページから遷移はしない予定。
iframeで会話ログも表示する。


未ログインならログインフォーム。
ログイン済みなら発言フォームを出す。
フォームの切り替えはテンプレートごと条件で切り替える。
それぞれ、index.phtml(ログイン前)、index2.phtml(ログイン済み)と分けておく。


まずはテストケース。

<?php
class IndexActionTest extends PHPUnit_Extensions_OutputTestCase {
    
    /**
     * アプリケーションディレクトリ
     */    
    const APP_DIR_PATH = '/var/project/chat/fw/site';

    public function setUp() {
        // インスタンス生成と初期化
        $this->act = new IndexAction();
        $config = array(
            'appDirPath' => self::APP_DIR_PATH,
            'renderer' => new Renderer()
        );
        $this->act->initialize($config);
    }
    
    public function testRunIndex() {
        // テンプレートのHTMLが出力されること
        ob_start();
        $render = new Renderer();
        $render->setup(self::APP_DIR_PATH . '/template/index.phtml');
        $render->run();
        $buff = ob_get_contents();
        ob_end_clean();
        $this->expectOutputString($buff);
        $this->act->run();
    }

    public function testRunIndex2() {
        // セッションにユーザ名を格納
        $name = 'Test';
        $_SESSION['identity'] = array('name' => $name);
        // セッションが存在する場合、専用テンプレートのHTMLが出力されること
        ob_start();
        $render = new Renderer();
        $render->setup(self::APP_DIR_PATH . '/template/index2.phtml', array('name' => $name));
        $render->run();
        $buff = ob_get_contents();
        ob_end_clean();
        $this->expectOutputString($buff);
        $this->act->run();
    }
}

で、アクションクラス。

<?php
/**
 * Indexアクション
 */
class IndexAction extends Action {

    /**
     * アクションを実行します
     * 
     * @return void
     */
    public function run() {
        // ビューの初期化
        $view = 'index';
        if(isset($_SESSION['identity'])) {
            // ログイン済みの場合、ビューを切り替えてパラメータセット
            $view = 'index2';
            $this->_set('name', $_SESSION['identity']['name']);
        }
        // ビューを描画
        $this->_render($view);
    }
}

そして、未ログイン用テンプレート。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <link rel="stylesheet" type="text/css" href="./css/chat.css" />
        <title>Chat</title>
    </head>
    <body>
        <div id="header">
        <div class="title">Chat</div>
        </div>
        <div id="content">
            <div id="form">
                <form method="post" action="index.php?a=login">
                    <table>
                        <tr>
                            <th>name</th>
                            <td>
                                <input type="text" name="name" style="width:160px;" />
                            </td>
                        </tr>
                    </table>
                    <input type="submit" value="Login" />
                </form>
            </div>
            <iframe id="log" src="index.php?a=log"></iframe>
        </div>
        <div id="footer">
            <div class="copyright">2010</div>
        </div>
    </body>
</html>

もうひとつ。ログイン済みのテンプレート。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <link rel="stylesheet" type="text/css" href="./css/chat.css" />
        <title>Chat</title>
    </head>
    <body>
        <div id="header">
            <div class="title">Chat</div>
        </div>
        <div id="content">
            <div id="form">
                <form method="post" action="index.php?a=comment" target="log_view">
                    <table>
                        <tr>
                            <th>name</th>
                            <td><?php $this->out($name); ?></td>
                        </tr>
                        <tr>
                            <th>comment</th>
                            <td>
                                <input type="text" name="comment" style="width:400px;" />
                                <input type="submit" value="Send" />
                                <input type="reset" value="Clear" />
                            </td>
                        </tr>
                    </table>
                </form>
                <form method="post" action="index.php?a=logout">
                    <input type="submit" value="Logout" />
                </form>
            </div>
            <iframe id="log" name="log_view" src="index.php?a=log"></iframe>
        </div>
        <div id="footer">
            <div class="copyright">2010</div>
        </div>
    </body>
</html>

テンプレートの中でif文使って分岐かけても良かったかも。
ほとんど同じ内容だし。

会話ログページを作る。

続いてiframeの中身の方、会話のログを表示するページ。
metaタグのRefresh使って定期更新させて、最新ログの表示と有効期限の延長を担当する。


まずはテストケース。

<?php
class LogActionTest extends PHPUnit_Extensions_OutputTestCase {
    
    /**
     * アプリケーションディレクトリ
     */    
    const APP_DIR_PATH = '/var/project/chat/fw/site';

    public function setUp() {
        // インスタンスの生成
        $this->act = new LogAction();
        $this->member = new Member(FILE_MEMBER, 300);
        $this->arc = new Archive(FILE_ARCHIVE, 30);
        // アクションの初期化
        $config = array(
            'appDirPath' => self::APP_DIR_PATH,
            'renderer' => new Renderer()
        );
        $this->act->initialize($config);
    }
    
    public function testRun() {
        // ユーザを追加
        $name = 'Test';
        $id = $this->member->add($name);
        $members = $this->member->getAll();
        $this->assertEquals(1, count($members));
        // ログを追加
        $this->arc->add($name, 'test');
        $logs = $this->arc->getAll();
        $this->assertEquals(1, count($logs));
        // ユーザとログのデータが反映された、テンプレートHTMLが出力されること
        ob_start();
        $render = new Renderer();
        $params = array(
            'members' => $members,
            'archives' => $logs
        );
        $render->setup(self::APP_DIR_PATH . '/template/log.phtml', $params);
        $render->run();
        $buff = ob_get_contents();
        ob_end_clean();
        $this->expectOutputString($buff);
        $this->act->run();
    }

    public function testRunWithIdentity() {
        // ユーザを追加
        $name = 'Test';
        $id = $this->member->add($name);
        $identity = array(
            'id' => $id,
            'name' => $name
        );
        // セッションにユーザデータをセット
        $_SESSION['identity'] = $identity;
        // 確認用に登録ユーザデータを取得して保持
        $members = $this->member->getAll();
        $user = $members[0];
        $this->assertEquals(1, count($members));
        // ログを追加
        $this->arc->add($name, 'test');
        $logs = $this->arc->getAll();
        $this->assertEquals(1, count($logs));
        // データが反映されたテンプレートHTMLが出力されること
        ob_start();
        $render = new Renderer();
        $params = array(
            'members' => $members,
            'archives' => $logs
        );
        $render->setup(self::APP_DIR_PATH . '/template/log.phtml', $params);
        $render->run();
        $buff = ob_get_contents();
        ob_end_clean();
        $this->expectOutputString($buff);
        // タイムスタンプ更新用に1秒待機
        sleep(1);
        $this->act->run();
        // セッションが存在する場合、ユーザのタイムスタンプが更新されていること
        $members = $this->member->getAll();
        $update = $members[0];
        $this->assertEquals(1, count($members));
        $this->assertEquals($user['id'], $update['id']);
        $this->assertEquals($user['name'], $update['name']);
        $this->assertLessThan($update['tstamp'], $user['tstamp']);
    }

    public function tearDown() {
        // テストデータの後始末
        if(file_exists(FILE_ARCHIVE)) {
            unlink(FILE_ARCHIVE);
        }
        if(file_exists(FILE_MEMBER)) {
            unlink(FILE_MEMBER);
        }
    }
}

続いてアクションクラス。

<?php
class LogAction extends Action {

    public function run() {
        $member = new Member(FILE_MEMBER, 300);
        if(isset($_SESSION['identity'])) {
            // ログイン済みの場合は期限を延長
            $identity = $_SESSION['identity']; 
            $member->update($identity['id']);
        }
        // 期限切れユーザを削除
        $member->gc();
        $arc = new Archive(FILE_ARCHIVE, 30);
        // 現在のログインメンバーと会話ログをViewに渡す。
        $this->_set('members', $member->getAll());
        $this->_set('archives', $arc->getAll());
        $this->_render();
    }
}

最後にテンプレート。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <meta http-equiv="Refresh" content="30;URL=index.php?a=log" />
        <link rel="stylesheet" type="text/css" href="./css/chat.css" />
        <title>Chat</title>
    </head>
    <body>
        <div id="members">
            <span>members:</span>
            <?php foreach($this->gets('members') as $member): ?>
            <span><?php $this->out($member['name']); ?></span>
            <?php endforeach; ?>
        </div>
        <hr/>
        <div id="archive">
            <?php foreach($this->gets('archives') as $row): ?>
            <p><?php $this->out($row['name']); ?> : <?php $this->out($row['comment']); ?> <?php $this->out(date('Y/m/d H:i:s', intval($row['date']))); ?></p>
            <?php endforeach; ?>
        </div>
    </body>
</html>

metaタグのRefresh使うの久しぶりで、フォーマット忘れてた。

あとは書き込みとかログイン周りで完成なはず。