CakePHPでリダイレクトする際のお作法 | A Day In The Boy's Life

A Day In The Boy's Life

とあるエンジニアのとある1日のつぶやき。

CakePHPのとあるアクションにアクセスしたら別ページに転送したくって下記のようなコードを書いてたんです。


public function index() {
    $location = "http://example.com/app/index";
    header("HTTP/1.1 302 Found");
    header("Location: $location");
}

で、何故かうまくいかなかったんで、Viewファイルも用意してそちらでもHTMLで転送するように書いていたんです。


<html>
<head>
<meta http-equiv="refresh" content="0;URL=http://example.com/app/index">
</head>
<body></body>
</html>

これでうまく転送されたんでほっといたんですが、よくよく見るとHTTPステータスが200を返されているわけです。

で、もう一度ちゃんとCakePHPのマニュアル読んだら色々間違っていたことがわかったんで、リダイレクト処理をする時のまとめのお話です。



CakePHPでページをリダイレクトする


最初のコードの問題の1つは、header 関数は呼び出した時点でヘッダを出力するのではないということです。

header()を呼び出してもヘッダ情報がセットされるだけで実際に出力されず処理が継続されるため(今まであまり意識することが無かったので気がつかなかったんですけど)、CakePHPはそのままViewを呼び出す処理を行います。

ですので、Viewファイルを作らなかったらMissingViewExceptionの例外が発生します。

一方でViewファイルを作ってしまったら正常にViewファイル内のHTMLが出力されるためHTTPステータスが200としてクライアントに返されます。

ってことで、アクションのコードだとheader()を出力した後にexitで抜けるとViewファイルを作らなくてもちゃんとリダイレクトされます。


もう1つの問題は、CakePHPはヘッダ出力用のメソッドが用意されていたということです。

header()でもリダイレクトはできるのですが、フレームワークを使っている以上、CakePHPのお作法に従ったほうがよさそうです。

ってことで、コードを書きのように書き換えればお作法に則ってリダイレクトさせることができます。


public function index() {
    $this->autoRender = false;
    $this->response->header('Location', "http://example.com/app/index");
}

autoRenderのメンバ変数はFALSEをセットすればViewを呼び出さないのでアクション内だけで処理を完結させることができます。

そして、$this->response->headerによってヘッダを制御 できます。

マニュアルを見ると


CakeResponse::header() が呼び出されなければヘッダは送られません。これらのヘッダはレスポンスが実際に送られるまでバッファリングされます。

って書いているので、ちゃんとCakePHPのお作法に従ってヘッダを送信したい場合は、CakeResponse::header()を使う必要があるようですね。


2014.05.14追記

CakeResponse::header()を使わなくてもController::redirectでできるよ!ってコメントを頂きました。

確かに下記のように書けばHTTPステータスコード付き(第2引数)でリダイレクト処理を行うことができます。


$this->redirect("/app/index", 303);

第3引数はexitフラグになっているようでFALSE(デフォルト)を指定すればリダイレクト後にexitで処理を抜けてくれます。

ですので、autoRenderの指定も必要なくなります。

また、コントローラやアクション名、パラメータなど細かい指定をしながらリダイレクトもできますので、プログラムから動的に制御したいといった場合などはこちらの方が便利かもしれませんね。

詳細は、マニュアル を参照してください。