かとじゅんの技術日誌

技術の話をするところ

PRGパターンをPHPで実装してみる

簡単にいうとPOSTで遷移した先のページで更新ボタンを押すともう一度POSTしますか?って聞かれる問題を解消できる実装パターンとして、PRGパターンというものがあります。

PRGパターンについては以下を参照してください。

  • こちらのP13,14あたりご覧ください。

私自身もPRGパターンのコードを学ぶため、PHP4でどのようになるか実装してみました。

問題を確認してみる

まず、簡単な入力画面から表示画面に遷移するアプリを作ってみたいと思います。
とりあえず、何も考えず通常の実装をおこなってみます。

userInput.php
<HTML>
<HEAD>
</HEAD>
<BODY>
        <FORM action="userView.php" method="post">
                <input type="text" name="userName" />
                <input type="password" name="password" />
                <input type="submit" value="Regist" />
        </FORM>
</BODY>
</HTML>
userView.php
<?php

$userName = $_POST["userName"];
$password = $_POST["password"];

?>
<HTML>
<HEAD>
</HEAD>
<BODY>
        username = <?php echo $userName; ?><br/>
        password = <?php echo $password; ?><br/>
</BODY>
</HTML>

まず、userInput.phpから適当に値を入れてsubmitしてください。そして、遷移先のページで更新ボタンを押す。もう一回POSTするか聞かれます。

PRGパターンにしてみる

userInput.php
<?php
        session_start();

        // セッション変数の破棄
        unset($_SESSION["userName"]);
        unset($_SESSION["password"]);

        // POSTされたら
        if ( $_SERVER["REQUEST_METHOD"] == "POST" ){
                // セッションに書き込みます
                $_SESSION["userName"] = $_POST["userName"];
                $_SESSION["password"] = $_POST["password"];
                // REDIRECTします
                header("Location: userView.php?redirect=true");
                exit;
        }
?>
<HTML>
<HEAD>
</HEAD>
<BODY>
        <FORM action="userInput.php" method="post">
                <input type="text" name="userName" />
                <input type="password" name="password" />
                <input type="submit" value="Regist" />
        </FORM>
</BODY>
<?php

session_start();

if ( $_GET["redirect"] == "true" ){
        // リダイレクト時だけ処理したい場合
}

$userName = $_SESSION["userName"];
$password = $_SESSION["password"];

?>
<HTML>
<HEAD>
<meta http-equiv="content-type" content="application/xhtml+xml; charset=EUC-JP" />
</HEAD>
<BODY>
        username = <?php echo $userName; ?><br/>
        password = <?php echo $password; ?><br/>
</BODY>
</HTML>

今度は、更新ボタンを押されても問題なく再表示されるはずです。
リダイレクトしているのにGETパラメータが指定されていない!?
GETパラメータ付でリダイレクトさせるとURLが変になるので、値はセッション経由で遷移先に渡すようです。
なので、リダイレクトした先でセッションをサクッと消してしまうと、更新ボタンが押されたときに値がないので表示できなくなります。しばらくはセッションに置いておくしかないと思います。画面数が多いアプリではセッションが膨れ上がらないように適当なタイミングで消去が必要になります。たとえば、TOPページに戻ったら削除とか、ユーザ管理から請求管理にジャンプしたら消すとか。ユースケースの切換え時に消すという感じです。

Teedaではこのようなことによく配慮して実装されていますね。ほんとによくできています。
http://d.hatena.ne.jp/higayasuo/comment?date=20070116#c