PRGパターン

PRG (POST-REDIRECT-GET) パターンの擬似コード書いてみました。各メソッドは名前から推察できるとおりHTTPの同名のメソッドに対応して呼び出されることを想定しています。

たとえば、ブラウザからPOSTした場合は以下のような感じ。(簡単のために厳密性に欠ける表現ですがあしからず) 

  1. ブラウザからサーバ
  2. サーバからブラウザ
    • Webページを返すよ。
    • cookieにPOSTの結果を 'result' として記しておいたよ(ちなみに失敗だったら 'code' としてエラーコードも記してあるよ)
    • リダイレクト先の URL も 'Location' ヘッダに記しておいたのでよろしく。
  3. ブラウザからサーバ
    • 2.で指示された URL から情報を GET するよ。(つまり2.で指示された URL にリダイレクトするってこと)
    • この際に2.で食べた cookie の内容も当然サーバに送るよ。
  4. サーバからブラウザ
    • Webページを返すよ。
    • 2.でセットした cookie を消してね。

PRGパターンの効能としてよく語られる「再送信の弊害の回避」は、上記における1.(=POST)ではなく3.(=GET)が再送信の対象となることによる効能、ということすね。


<?php

class Controller()
{
    public function get()
    {   
        $result = $this->cookie('result');
        if ($result !== '') {
            $this->view->set(array('result' => ($result === 'success')));
            $this->cookie('result')->delete();
            $code = $this->cookie('code');
            if ($code !== '') {
                $this->view->set(array('code' => (int)$code));
                $this->cookie('code')->delete();
            }   
        }   
        $this->view->set('resource' => $this->resource);
        $this->view->render();
    }   

    public function post()
    {   
        $this->_process('create');
    }   

    public function put()
    {   
        $this->_process('update');
    }   

    public function delete()
    {   
        $this->_process('delete');
    }   

    private function _process($process)
    {   
        $result = true;
        try {
            $this->resource->$process();
        } catch (Exception $e) {
            $result = false;
            $this->cookie('result')->set('failure');
            $this->cookie('code')->set($e->getCode());
        }
        $result && $this->cookie('result')->set('success');
        $this->redirect($this->resource->link);
    }
}