REST ? CakePHP Cookbook v2.x documentation
http://book.cakephp.org/2.0/ja/development/rest.html
湘南社中テクニカルブログ: <b>Server</b>CakePHPでRESTのJSON APIを作成する
http://blog.shonanshachu.com/2012/01/cakephprestjson-api.html
Cakephp 2.x の REST API を作成してみる ≪ Hello My World
http://plmin.us/blog/2013/01/29/cakephp-2-x-rest-api/
CakePHP2.0で配列をXMLに変換し出力する | Code Life
http://code-life.net/?p=1096
簡単に言えば、HTTPのURLを指定して、戻り値をXMLやJSONで取得する。XML-PRCの場合は、送るときにXMLデータをPOSTするのだが、RESTの場合はURLを使う。いわゆる ASP.NET MVCのWeb APIと同じ。POSTやPUTなどを使うが、手軽のはPOSTのほう。メソッド名さえ揃えてしまえば、POSTオンリーでもよい気がする。
CakePHP には、もともとRESTの機能を持っていて、XMLで返すことが簡単にできる…はずなのだが、結構苦労する。というのも、
REST ? CakePHP Cookbook v2.x documentation
http://book.cakephp.org/2.0/ja/development/rest.html
を見ると、REST の方法には、
- _serialize を使う方法
- xml/ フォルダを作る方法
と2つ載っているものの、_serialize の方法は動かない。正確に言うと、内部データを XML に変換しているときにこけてしまう…う~ん、これがバグなのかどうかはわからない。なので、xml/ フォルダに index.ctp 等の View を作って対応する。
新しい View を作ると便利なのが、元の View(ブラウザで開くときのview)を残しておくことができることで、_serialize を使うと、Controller 自身を書き換えてしまうことになるので、ブラウザからちょいちょいと修正したいときに不便。なので、ブラウザ用の view は残したままで、REST 用の view≒XMLを返す view を新しく追加する、という方式のほうが保守的によい。ただし、セキュリティ的にはブラウザ用の view もロックをしないといけない。データの更新機能は管理者レベルにしたいことが多いので、実運用の場合は別途ロックを掛ける必要がある。
■app/Config/routes.php を書き換える
app/Config/routes.php に Router::parseExtensions(); を追加する。Router::mapResources(‘recipes’); のようにテーブル名をマッピングするようにも書いてあるのだが、これがなくても動く。ちなみに、ドキュメントでは「require CAKE … よりも前に」となっているが、後ろでも動作する。が、先頭に書いておくのがベター。
//Router::mapResources('Stores'); // 無くてもok Router::parseExtensions('xml'); ...
■app/Controller/AppController.php を書き換える
本来は、RequestHandler を個別のControllerに入れるべきなのだが、数が多いので AppController に入れてしまう。こうすると全てのコントローラーとビューで xml/ フォルダへの routes が有効になる。
class AppController extends Controller { public $components = array('RequestHandler'); }
■xml/index.ctp を作る
View/Stores/ に xml というフォルダを作って、その中に index.ctp などを新しく作る。
+ View + Stores - index.ctp - view.ctp - edit.ctp - add.ctp + xml - index.ctp - view.ctp
こうすることで、http://localhost/Stores/ を呼び出したときは View/Stores/index.ctp を、http://localhost/Stores.xml を呼び出したときは View/Stores/xml//index.ctp が呼び出される。多分、xml は小文字(フォルダー名と同じ)にしないとダメだと思う。実験環境は windows 上なので、XML でも xml で通るのだが、Unix 環境だと区別すると思われる。
View/Stores/xml/index.ctp
$items = array('items'= array('item'=> $stores)); $xml = Xml::fromArray(array('response'=>$items)); echo $xml->asXML();
XML形式で返すように asXML メソッドを使う。Xml::fromArray へ配列を渡す形式は、
XML ? CakePHP Cookbook v2.x documentation
http://book.cakephp.org/2.0/ja/core-utility-libraries/xml.html
を参照。CakePHP の 1.x と 2.x で渡すときの形式が違っているので注意が必要。おそらく _serialize の方法がダメなのは、ここを間違っていると思われ。
返す結果は、以下のように items タグの中に item が複数ある形式になる。「$stores」のところは、Controller で set している名前に随時変更する。
<response> <items> <item> <Store> <ID>1</ID> <SellingCompanyID>1</SellingCompanyID> <AreaGroupID>26</AreaGroupID> ... <Store> <item> <item> ... </item> </items> </response>
view の場合は、$store を元に単数で返せば ok
<!--?php $item = array('item'=--> $store); $xml = Xml::fromArray(array('response'=>$item)); echo $xml->asXML();
■実行してみる
□http://localhost/Stores.xml の場合
□http://localhost/Stores/view/1 の場合
□http://localhost/Stores/view/1.xml の場合
index.ctp と view.ctp 自体はコピーアンドペーストで作れるので、適当なスクリプトを作って増産しておけばよい。編集機能(edit/add と DELETE)の場合は、むやみに使われないようにフォーム認証(BASIC認証)と合わせて作ることになるので、別途 C# のクライアントを作って試してみる。
~~~
追記
元のコントローラーを使っているのでページング機能が有効になっています。なので、デフォルトでは最初の20件しか取れません。このデータ自体は1万件以上あるので、すべてを取ってきてもダメ、できれば limit を指定したいところですね。全ページ数を取得するかどうかはさておき、「http://localhost/Stores/index/page:2.xml」のようにすれば、2ページ目も取得できます。
指定ページ以上を取得しようとして page:999.xml のようにすると、404 のエラーが発生するので、例外が出ている模様。このあたりを気を付ければ全件を取得できる?