この記事を三行にまとめると
JSONデータを返したい場合があると思います3系を使う方が良さそうですぞ
13日は金曜になることが一番多いらしいですよ
APIの開発などでJSONデータを返したい場合があると思います。今日はそれをCakePHPでやってみましょう。わりとお手軽にできるので、知っとくと便利かも。
今回は「http://norm-nois.com/api/index」にリクエストを投げてJSONデータを取得する仕様を考えてみます。
さっそくコードを書いてみましょう。
基本的には通常のページを作るのと同じですね。
ポイントは、ApiController.phpの方で「RequestHandler」コンポーネントを読み込むことと、「_serialize」に値をセットすること、それからroutes.phpのRouter::connectの中身ですね。今回は「/api/index」のページのみJSONに対応するためにこんな書き方をしましたが、もし全部のページに対応したいのならこう↓。
ようは「’ext’ => ‘json’」という要素を追加すればオッケーってことですね。
上記のように書いた場合、画面にはこんな↓JSONデータが出力されます。
パッと見何て書いてあるか分かりませんが「あかつきのお宿」と表示されています。json_encode()はデフォルトだとマルチバイト文字をエスケープするので、こんな感じになってます。エスケープしない方法は後ほどやりましょう。
今は文字列だけを表示しましたが、もちろん配列を渡すことも可能です。
「array(‘site’ => ‘あかつきのお宿’)」がJSONデータになった状態です。
複数の変数を渡す場合も書き方は同じ。
サイト名とURLのJSONデータです。
やり方は二つあります。
一つはコアライブラリにある「JsonView.php」の中身をいじること。「/Cake/View/JsonView.php」を「App/View/JsonView.php」にコピって「_serialize()」というメソッドをちょちょっと書き換えます。
json_encode()の第二引数に「JSON_UNESCAPED_UNICODE」をセットするとエスケープをキャンセルできます。
ちなみに今は出力内容が一行で表示されていますが、print_r()に対するpr()のように、整形して見たい場合には「JSON_PRETTY_PRINT」をセットします。
二つ以上指定する場合は、コンマ区切りでも配列でもなく「|」で区切ります。バーティカルバーとかバーティカルラインと呼ばれるやつです。俺も今回初めて読み方知ったわ。
ちなみにデバッグを出力する設定にしている場合は、Cakeさんが勝手にJSON_PRETTY_PRINTをつけて表示してくれます。
もう一つのやり方は、ビューファイルを使って自分でjson_encode()を行うこと。
これまでの書き方はビューファイルの作成を必要としませんでした。でも「$this->set(‘_serialize’, ***)」をコントローラーで書かない場合は、通常と同じようにctpファイルが読み込まれます。
気をつけなきゃいけないのは、ビューファイルの置く場所。通常のページを表示する場合はApiフォルダの下にindex.ctpを作りますが、ここではApiフォルダの下にさらに「json」というフォルダを作って、その中にindex.ctpを入れます。
これはroutes.phpに書いた「’ext’ => ‘json’」が関係してます。もし「’ext’ => ‘xml’」と書けば、読み込まれるビューファイルは「Api/View/xml/index.ctp」になります。
ちなみに「’ext’ => ‘aaa’」とかにすると、そんな形式のもんはねえって判断されて、普通に「Api/View/index.ctp」が読み込まれるようです。
その場合は、routes.phpにこんな設定を書く。
この二行を追加すれば「/api/index.json」に限らずどのURLでも「.json」をつけることが可能です。json以外でも、例えば「setExtensions(array(‘pdf’))」と書けば「http://norm-nois.com/api/index.pdf」といったURLも使えるようになります。
コントローラーやビューの書き方は先ほどと一緒です。「_serialize」に値をセットすればビューファイルが不要になるし、JsonView.phpファイルをいじればエスケープや整形表示ができるのも同様。
「Router::connect(‘/api/index’, array(‘controller’ => ‘api’, ‘action’ => ‘index’, ‘ext’ => ‘json’));」を削除せずに残しておいた場合は、「http://norm-nois.com/api/index」と「http://norm-nois.com/api/index.json」のどちらもJSONを受け取るページになります。どのパターンが良いかは仕様次第ですかね。
次はCakePHP3です。書き方がちょっと違うだけでやり方はCakePHP2とそんな変わりませんが、CakePHP3の方がやや楽になってます。
CakePHP2と違うのは、コントローラーで「_serialize」に値をセットする必要がないところですね。CakePHP3では「$this->set()」でセットされた値を自動的にJSONデータで出力してくれます。テンプレートファイルも不要。
routes.phpもちょっと変わってます。「’ext’ => ‘json’」じゃなくて「’_ext’ => ‘json’」です。先頭のアンダーバーを忘れずに。
これだけでOKです。コアファイルをいじったりテンプレートを自作する必要はナッシング。
楽勝だーね。
ふいー。相変わらず長くてすみません。でも作業自体はそんな大変ではないので、だばーっと流し読みしながらちゃちゃーっと実装しちゃってくださいな。
2に比べると3の方が楽かなーって感じですね。だからJSON出力用の何かを新規に作りたいのであれば、3系を使う方が良さそうですぞ。
その他のCakePHP3を触ってみましたの記事はこちら
まとめという名の箸休め
今回は「http://norm-nois.com/api/index」にリクエストを投げてJSONデータを取得する仕様を考えてみます。
JSONデータの出力(CakePHP2)
まずはCakePHP2で作る場合です。やるべきことは、ApiController.phpを用意することと、/api/indexにアクセスした場合は強制的にjsonデータを出力するようにroutes.phpに設定を書くことです。さっそくコードを書いてみましょう。
//ApiController.php
class ApiController extends AppController {
public $components = array('RequestHandler');
public function index() {
$site = 'あかつきのお宿';
$this->set(compact('site'));
$this->set('_serialize', 'site');
}
}
//routes.php
Router::connect('/api/index', array('controller' => 'api', 'action' => 'index', 'ext' => 'json'));//この一行を追加
基本的には通常のページを作るのと同じですね。
ポイントは、ApiController.phpの方で「RequestHandler」コンポーネントを読み込むことと、「_serialize」に値をセットすること、それからroutes.phpのRouter::connectの中身ですね。今回は「/api/index」のページのみJSONに対応するためにこんな書き方をしましたが、もし全部のページに対応したいのならこう↓。
Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display', 'ext' => 'json'));
ようは「’ext’ => ‘json’」という要素を追加すればオッケーってことですね。
上記のように書いた場合、画面にはこんな↓JSONデータが出力されます。
"\u3042\u304b\u3064\u304d\u306e\u304a\u5bbf"
パッと見何て書いてあるか分かりませんが「あかつきのお宿」と表示されています。json_encode()はデフォルトだとマルチバイト文字をエスケープするので、こんな感じになってます。エスケープしない方法は後ほどやりましょう。
今は文字列だけを表示しましたが、もちろん配列を渡すことも可能です。
//$this->set('_serialize', 'site')を書き換える
$this->set('_serialize', array('site'));
//出力結果
{"site": "\u3042\u304b\u3064\u304d\u306e\u304a\u5bbf"}
「array(‘site’ => ‘あかつきのお宿’)」がJSONデータになった状態です。
複数の変数を渡す場合も書き方は同じ。
//ApiController.php
$site = 'あかつきのお宿';
$url = 'http://norm-nois.com/api/index';
$this->set(compact('site', 'url'));
$this->set('_serialize', array('site', 'url'));
//出力結果
{"site": "\u3042\u304b\u3064\u304d\u306e\u304a\u5bbf","url": "http:\/\/norm-nois.com\/api\/index"}
サイト名とURLのJSONデータです。
エスケープキャンセル(CakePHP2)
ではマルチバイトのエスケープのキャンセルをば。やり方は二つあります。
一つはコアライブラリにある「JsonView.php」の中身をいじること。「/Cake/View/JsonView.php」を「App/View/JsonView.php」にコピって「_serialize()」というメソッドをちょちょっと書き換えます。
//JsonView.php
protected function _serialize($serialize) {
〜中略〜
//return json_encode($data);を書き換え
return json_encode($data, JSON_UNESCAPED_UNICODE);
}
//出力結果
{"site":"あかつきのお宿","url":"http:\/\/norm-nois.com\/api\/index"}
json_encode()の第二引数に「JSON_UNESCAPED_UNICODE」をセットするとエスケープをキャンセルできます。
ちなみに今は出力内容が一行で表示されていますが、print_r()に対するpr()のように、整形して見たい場合には「JSON_PRETTY_PRINT」をセットします。
//JsonView.php
return json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
//出力結果
{
"site": "あかつきのお宿",
"url": "http:\/\/norm-nois.com\/api\/index"
}
二つ以上指定する場合は、コンマ区切りでも配列でもなく「|」で区切ります。バーティカルバーとかバーティカルラインと呼ばれるやつです。俺も今回初めて読み方知ったわ。
ちなみにデバッグを出力する設定にしている場合は、Cakeさんが勝手にJSON_PRETTY_PRINTをつけて表示してくれます。
もう一つのやり方は、ビューファイルを使って自分でjson_encode()を行うこと。
これまでの書き方はビューファイルの作成を必要としませんでした。でも「$this->set(‘_serialize’, ***)」をコントローラーで書かない場合は、通常と同じようにctpファイルが読み込まれます。
//ApiController.php
$site = 'あかつきのお宿';
$url = 'http://norm-nois.com/api/index';
$data = array('site' => $site, 'url' => $url);
$this->set(compact('data'));
//View/Api/json/index.ctp
<?php echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) ?>
//出力結果
{
"site":"あかつきのお宿",
"url":"http:\/\/norm-nois.com\/api\/index"
}
気をつけなきゃいけないのは、ビューファイルの置く場所。通常のページを表示する場合はApiフォルダの下にindex.ctpを作りますが、ここではApiフォルダの下にさらに「json」というフォルダを作って、その中にindex.ctpを入れます。
これはroutes.phpに書いた「’ext’ => ‘json’」が関係してます。もし「’ext’ => ‘xml’」と書けば、読み込まれるビューファイルは「Api/View/xml/index.ctp」になります。
ちなみに「’ext’ => ‘aaa’」とかにすると、そんな形式のもんはねえって判断されて、普通に「Api/View/index.ctp」が読み込まれるようです。
URLに.jsonをつける(CakePHP2)
例えば「http://norm-nois.com/api/index」では通常のHTMLページを表示して、「http://norm-nois.com/api/index.json」というURLでJSONを受け取れるようにしたいなんて場合もあるかもしれない。その場合は、routes.phpにこんな設定を書く。
//以下の一行を削除
Router::connect('/api/index', array('controller' => 'api', 'action' => 'index', 'ext' => 'json'));
//以下の二行を追加
Router::parseExtensions();
Router::setExtensions(array('json'));
この二行を追加すれば「/api/index.json」に限らずどのURLでも「.json」をつけることが可能です。json以外でも、例えば「setExtensions(array(‘pdf’))」と書けば「http://norm-nois.com/api/index.pdf」といったURLも使えるようになります。
コントローラーやビューの書き方は先ほどと一緒です。「_serialize」に値をセットすればビューファイルが不要になるし、JsonView.phpファイルをいじればエスケープや整形表示ができるのも同様。
「Router::connect(‘/api/index’, array(‘controller’ => ‘api’, ‘action’ => ‘index’, ‘ext’ => ‘json’));」を削除せずに残しておいた場合は、「http://norm-nois.com/api/index」と「http://norm-nois.com/api/index.json」のどちらもJSONを受け取るページになります。どのパターンが良いかは仕様次第ですかね。
JSONデータの出力(CakePHP3)
CakePHP2の説明だけでだいぶ長くなっちゃいましたね。さーせん。コーヒーブレイクでも入れて、もうちょっとだけおつき合いくだせえ。次はCakePHP3です。書き方がちょっと違うだけでやり方はCakePHP2とそんな変わりませんが、CakePHP3の方がやや楽になってます。
//ApiController.php
namespace App\Controller;
use App\Controller\AppController;
class ApiController extends AppController {
public function initialize() {
parent::initialize();
$this->loadComponent('RequestHandler');
}
public function index() {
$site = 'あかつきのお宿';
$url = 'http://norm-nois.com/api/index';
$this->set(compact('site', 'url'));
}
}
//routes.php
Router::scope('/', function ($routes) {
$routes->connect('/api/index', ['controller' => 'Api', 'action' => 'index', '_ext' => 'json']);
$routes->fallbacks('DashedRoute');
});
//出力結果
{"site":"\u3042\u304b\u3064\u304d\u306e\u304a\u5bbf","url":"http:\/\/norm-nois.com\/api\/index"}
CakePHP2と違うのは、コントローラーで「_serialize」に値をセットする必要がないところですね。CakePHP3では「$this->set()」でセットされた値を自動的にJSONデータで出力してくれます。テンプレートファイルも不要。
routes.phpもちょっと変わってます。「’ext’ => ‘json’」じゃなくて「’_ext’ => ‘json’」です。先頭のアンダーバーを忘れずに。
エスケープキャンセル(CakePHP3)
CakePHP2に比べると、3の場合のエスケープキャンセルは簡単です。コントローラーに次の一行を追加するだけでOK。//ApiController.php
public function index() {
$site = 'あかつきのお宿';
$url = 'http://norm-nois.com/api/index';
$this->set(compact('site', 'url'));
//この一行を追加
$this->viewVars['_jsonOptions'] = JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT;
}
//出力結果
{
"site": "あかつきのお宿",
"url": "http:\/\/norm-nois.com\/api\/index"
}
これだけでOKです。コアファイルをいじったりテンプレートを自作する必要はナッシング。
URLに.jsonをつける(CakePHP3)
これもroutes.phpに一行追加するだけ。//routes.php
Router::scope('/', function ($routes) {
//以下の一行を追加
$routes->extensions(['json']);
});
楽勝だーね。
ふいー。相変わらず長くてすみません。でも作業自体はそんな大変ではないので、だばーっと流し読みしながらちゃちゃーっと実装しちゃってくださいな。
2に比べると3の方が楽かなーって感じですね。だからJSON出力用の何かを新規に作りたいのであれば、3系を使う方が良さそうですぞ。
その他のCakePHP3を触ってみましたの記事はこちら
まとめという名の箸休め