CakePHP3を触ってみました 〜ジェイソンあらわる〜

13日は金曜が一番多いらしいぞ

おまとめ三行

JSONデータを返したい場合があると思います
3系を使う方が良さそうですぞ
13日は金曜になることが一番多いらしいですよ

APIの開発などでJSONデータを返したい場合があると思います。今日はそれをCakePHPでやってみましょう。わりとお手軽にできるので、知っとくと便利かも。

今回は「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を触ってみましたの記事はこちら
まとめという名の箸休め

まだコメントはいただけてないみたい……
 もしかしたら何か関連しているかも?