AkActionController#redirectTo()メソッドと、Viewのrendering処理について
あるURLにリダイレクトさせたい場合(Akelosの場合はデフォルトでHTTP 302 でLocationヘッダーを送出)、AkActionController#redirectTo()メソッドを使います。
ところで、redirectTo()を呼んだ後、Viewのrendering処理は発生するのでしょうか?
確かにブラウザ側としては302+Locationで、与えられたURLにジャンプすればおしまいですが、PHP側としては、302を出力したなら以降の処理はskipしたいのが正直なところです。例えばrenderingで、layoutやviewの中に、副作用を及ぼす処理が入っているかもしれませんし。
結論としては、viewやlayoutのレンダリングは行われません。もちろんcontrollerのaction中で、redirectTo()の後ろにPHPスクリプトを続けていれば実行されてしまいますが、レンダリングは実行されません。
例:
class FooController extends AkActionController { function bar() { ... if (...) { $this->redirectTo('http://...'); } ... // ここはredirectTo()を呼んだ後、実行されてしまいます。 } }
この例であれば、どうせlayoutやviewはレンダリングされないのですから、即returnしてしまった方が安心です。
修正後:
class FooController extends AkActionController { function bar() { ... if (...) { $this->redirectTo('http://...'); return; } ... // redirectTo()を呼べばreturnされるので、実行されません。 } }
裏舞台をのぞいてみましょう。
AkActionController :
<?php ... function redirectTo($options = array(), $parameters_for_method_reference = null) { ... $this->Response->redirect($options); // ここで実際に HTTP 302 + Locationヘッダーが送出 $this->Response->redirected_to = $options; $this->performed_redirect = true; ... } function _hasPerformed() { return !empty($this->performed_render) || !empty($this->performed_redirect); } function process(...) { ... $this->performActionWithFilters($this->_action_name); if (!$this->_hasPerformed()){ $this->_enableLayoutOnRender ? $this->renderWithLayout() : $this->renderWithoutLayout(); } $this->Response->outputResults(); } ...
redirectTo()で実際にヘッダーが送出されると、"performed_redirect"フラグがONになります。AkActionControllerの起動I/Fであるprocess()メソッド中では、performActionWithFilters()でアクションメソッドを実行した後、_hasPerformed()メソッドでperformed_redirectフラグをチェックし、OFFの時にrendering処理を呼び出しています。
これにより、controllerのaction中でひとたび(あるいはfilter中で)redirectTo()が呼ばれれば、基本的にはrendering処理は行われない事が保証されます。
では、例えば次のようなコードで無理矢理render()を呼ぶことでレンダリング処理をさせる事はできるか?ですが・・・
$this->redirectTo('http://...'); $this->render('....');
render()メソッドの中はかなり入り組んでて、パラメータを調整し直してはrender****に委譲して、さらにその中を追ってみるとまたrenderWith(out)Layout()を呼んでいたりして非常に混沌としていますが・・・。
単純にrender()を呼び出しただけであれば、render()メソッドの冒頭の以下の分岐ではじかれる筈です。(partialbは別格)
function render($options = null, $status = 200) { if(empty($options['partial']) && $this->_hasPerformed()){ $this->_doubleRenderError(Ak::t("Can only render or redirect once per action")); return false; } ...
という感じで、redirectTo()を呼ぶ後のrendering処理は基本的に、Akelos側で自動的に無効化してくれるものと思って大丈夫そうです。