CakePHP御存知の通りはControllerとModelで処理を書いていき、そこからViewをレンダーします。
ですが先日、Ajaxのレスポンスで処理結果のステータスとHTMLの両方をJSONで返したい!ということがありました。
こういう場合はViewでヒアドキュメントを利用すればゴリゴリ書くことも可能ですがメンテナンス性が低下します(後述)
今回はControllerの中でViewをレンダーし、HTMLをController内で扱う方法をご紹介します。
Viewを正しく使ってJSONを返却する
恐らくこれが正攻法だと思います。ですがメンテナンス性としてもあまり好きではありません。Controller側ではLayoutのレンダーを無効化し、アクションのViewのみをレンダーさせています。また、CakeResponse($this->response)でContentTypeをapplication/jsonに指定しています。
class TestController extends AppController { public function json_response() { $this->autoLayout = false; // JSON形式 $this->response->type('json'); } }
これに対し、ViewではヒアドキュメントでHTML記述し、json_encodeしています。
$html = <<<HTML <div> <p>なんかのダイアログ</p> <button>閉じる</button> </div> HTML; echo json_encode(array( 'status' => '成功しました', 'html' => $html ));
ヒアドキュメントで書かなくとも外部ファイル(Element)に切ったり、ViewBlockを使って$htmlに入れてしまう方法もあると思います。
echo json_encode(array( 'status' => '成功したよ!', 'html' => $this->element('dialog') ));
でも、なんか違うじゃん!ヒアドキュメントとか美しくないじゃん!
Elementでも外部ファイルいちいち切ったらファイル増えるじゃん!
Viewがこれだとメンテナンスもよろしくないですね。
json_encodeとかしちゃってるのもViewのお仕事ではない気がします。個人的に。
Controller上でレンダーしたViewのHTMLを取得する
今回はViewにダイアログのHTMLを記述して、Controller上でそのViewをレンダーしながらjsonで固める処理を書きます。
Controller上でのみ処理を完結させます。
<div> <p>なんかのダイアログ</p> <button>閉じる</button> </div>
class TestController extends AppController { public function ajaxTest() { // Viewのレンダーを無効化 $this->autoRender = false; // ContentTypeをJSONにする $this->response->type('json'); // Viewを生成。Controllerの状態が引き継がれる $View = new View($this); // $View->viewPath = 'Viewのフォルダ名'; $View->set('Viewに渡す変数名', '変数値'); $html = $View->render('ajax_test', false); return json_encode(array( 'status' => '成功したよ!', 'html' => $html )); } }
Controllerの$autoRenderをfalseにするとreturnの値をレスポンスにすることができます。これを利用してjson形式のデータを返しています。
View::viewPathにはControllerの名前がデフォルトで入るので、必要に応じてフォルダを指定します。
View::render()の第一引数はviewファイル名,第二引数にはレイアウトファイル名を指定します。
レイアウトを使用しない場合は第二引数にfalseを渡してください。
これでJSONのレスポンスにHTMLを含めることができました。
以上、少し裏ワザ的な手法ですが個人的にしっくりくるAPIの実装でした。
コメント