エラーメッセージを出すタイミング 2

エラーがあったら入力画面にエラーメッセージを出したい。
で、_forward() を使って、確認画面から入力画面に吹っ飛ばして表示できた。
でも表示されている画面(テンプレート)は入力画面だけど、URI は確認画面の URI で表示されてしまう。
エラーメッセージを出すタイミング - Memo


他の方法が無いか考えてみた。
常に indexAction() に遷移して、そこから _forward() で他のアクションに遷移させるアプローチを思いついた。


index.tpl

<form action='index' method='post'>
  <input type='text' name='hoge' value='' />
  <input type='submit' name='confirm' value=' 確認' />
</form>

submit ボタンの名前を遷移先のアクション名にする。


PHP のソースはこんな感じ。

<?php
class IndexController extends My_Controller_Action
{
    $_steps;
    $_currentStep;

    public function init()
    {
        parent::init();
        // フォームの遷移するアクション名を登録
        $this->_steps = array('index', 'confirm', 'finish');
    }
    public function preDispatch()
    {
        parent::preDispatch();
        $params = $this->getAllParams();
        if (is_array($params)) {
            foreach ($this->_steps as $val) {
                if (array_key_exists($val, $params)) {
                    // 現在のアクション名を登録する
                    $this->_currentStep = $val;
                    break;
                }
            }
        }
        // index 以外のアクションに URI 直叩きされた場合は、
        // indexAction() にリダイレクトする
        if ($this->_currentStep == '' && $this->_action != 'index') {
            $uri = DIRECTORY_SEPARATOR . $this->_module 
                 . DIRECTORY_SEPARATOR . $this->_controller;
            $this->_redirect($uri, array('exit'));
        }
        // 振り分け処理
        if ($this->_currentStep != ''
            && $this->_currentStep != $this->_action) {
            if ($this->validate()) {
                $this->forward($this->_currentStep);
                return;
            }
        }
    }
    public function validate()
    {
        // 入力チェックの処理を書く
        // エラーがある場合は、テンプレートにエラーメッセージを assign する
        // ここでは省略
        return true;
    }
    public function indexAction()
    {   
        $param = $this->getParam('hoge');
        $this->assign('hoge', $param);
    }
    public function confirmAction()
    {   
        $param = $this->getParam('hoge');
        $this->assign('hoge', $param);
    }
}

テンプレートの submit ボタンの name 属性を次の遷移先のアクション名にすることで、
preDispatch() でinit() で登録された一連の遷移のフロー(と言っていいのかな。。)と一致したら、遷移先のアクション名を属性に登録しておく。


で、各画面で遷移するタイミングで必ず入力チェックを行い、
チェックに通ったら、遷移先のアクションに _forward() してやる。
チェックが通らなかったら、当然 indexAction() のままで表示される。


なんで preDispatch() でやっているかというと、今は直にコントローラに書いているけど、拡張性を考えたら、親クラスでやった方が良い。
で、遷移フローの配列の設定は各コントローラで設定したい。
init() は preDispatch() より先に呼ばれるので、振り分け処理は preDispatch() でやった。


これで入力した値に不備があっても、入力画面にエラーメッセージも出るし、URI も気持ち悪くないw
ただし、エラーメッセージを出すタイミング - Memo で取り上げた方法に比べて振り分け処理が入っているから速度面では落ちる。