とあるプロジェクトで、AngularJSを使いました。
バックエンドはCakePHPを使いました。
例えば、AngularJSからデータをCakePHPにPostで送信した際に$this->request->data
に値が入っていませんでした。
なので調べた。
ちなみにAngularは触ってまだ2日目の超初心者です。
AngularJS1.4 => CakePHP2.xへ渡し方&受け取り方
環境
- AngularJS 1.4.6
- CakePHP 2.8
AngularJS側の実装
var app = angular.module('application', []); app.controller('AppController', function($scope, $http) { $scope.updateButton = function() { var url = '/posts/save'; var saveData = { post: '適当なデータ' }; $http.post( url, saveData, {} ).then(function(response) { // 成功処理 console.log(response); }, function(response) { // 失敗処理 console.log(response); }); }; });
これでリクエストすることができた。メソッドはPostです。
が、何故かCakePHPの$this->request
に値が入っておらん。
なぁーぜぇー?(’・ω・`)
色々調べた&教えてもらったこと
どうやらリクエストのbody
のデータはphp://input
で取得できるっぽい
<?php ... $data = json_decode(file_get_contents('php://input'), true);
これで取得できるようになった。
が、せっかくCakePHPでリクエストクラスが用意されている&AngularJSと連携するControllerだけ書き方が変わるのはいかがなものかなと思った。
ホントはとれるんじゃない?なんてこと思ったので、CakePHPのLibraryを読んでみることにした。
CakePHP側でデバッグ
リクエスト側ってどんなして処理してんの?ってことでソースを読んでみました。
<?php ... // 164行目ぐらい protected function _processPost() { if ($_POST) { $this->data = $_POST; } elseif (($this->is('put') || $this->is('delete')) && strpos(env('CONTENT_TYPE'), 'application/x-www-form-urlencoded') === 0 ) { $data = $this->_readInput(); parse_str($data, $this->data); }
CakeRequest.php
のクラスがオブジェクト化される際のコンストラクタでCakeRequest->_processPost()
が呼ばれる。
んで、そのコードはしょっぱなから、$_POST
変数の存在をチェックしている。もし存在するなら$this->data
、よく使う$this->request->data
に値をそのまま渡している。
AugularJSでリクエストを飛ばした場合の時、$_POST
に値が入っていないというよりは、$_POST
が宣言されていない。
はへぇーーーー°ω° そんなことってあるのね。
まぁそれはさておき。次の条件が気になった。
$data = $this->_readInput();
<-これ。なかにジャンプしてみると、、、
<?php ... // 1075行目ぐらい protected function _readInput() { if (empty($this->_input)) { $fh = fopen('php://input', 'r'); $content = stream_get_contents($fh); fclose($fh); $this->_input = $content; } return $this->_input; }
おお!これじゃねーの? ここを処理させることができればええんじゃね?
っつーことで、条件を再度見直し。
($this->is('put') || $this->is('delete')) && strpos(env('CONTENT_TYPE'), 'application/x-www-form-urlencoded')
単純です。HTTPのメソッドがput
かdelete
で、且つ、content-type
の文字列でapplication/x-www-form-urlencoded
が部分一致すれば良いとのこと。
では、そうなるようにAngularのほうを書き換える。
var app = angular.module('application', []); app.controller('AppController', function($scope, $http) { $scope.updateButton = function() { var url = '/posts/save'; var saveData = { post: '適当なデータ' }; // メソッドを`put`へ変更 $http.put( url, saveData, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } } ).then(function(response) { // 成功処理 console.log(response); }, function(response) { // 失敗処理 console.log(response); }); }; });
こうすれば、先ほどの$this->_readInput()
への条件がtrue
になった!
これが正解か!
<?php ... // 164行目ぐらい protected function _processPost() { if ($_POST) { $this->data = $_POST; } elseif (($this->is('put') || $this->is('delete')) && strpos(env('CONTENT_TYPE'), 'application/x-www-form-urlencoded') === 0 ) { $data = $this->_readInput(); // ここでリクエストを取得 parse_str($data, $this->data); // ? なにこれ? }
???(・ω・)
$this->request->data
には、値が入らないっぽい。
とりあえず、$this->request->_input
の中に値が入ることはわかった。
これで、AngularJSを使ったとしても、CakePHPのRequestクラスを使って実装することができる。...
でき。。。ない!?
<?php ... // 125行目 protected $_input = '';
( ゚∀゚)・∵. グハッ!!
これじゃとれねぇ。。。(外部クラスからアクセスできないから)
じゃあどうすれば。。。
ソース読んでいるとあることに気づいた。
<?php ... // 1000行目ぐらい public function input($callback = null) { $input = $this->_readInput(); $args = func_get_args(); if (!empty($args)) { $callback = array_shift($args); array_unshift($args, $input); return call_user_func_array($callback, $args); } return $input; }
・・・・・・・・・・・・・。
googleで「CakePHP request input」で検索
REST を採用しているアプリケーションではURLエンコードされていないpost形式でデータを交換することがしばしばあります。 CakeRequest::input() を使っているどんな形式であっても入力データを読み込むことができます。 デコード関数が提供されることでデシリアライズされたコンテンツを受け取ることができます。
Oh...
<?php class Posts extends AppController { public function save() { // 取れた!ヾ(*´∀`*)ノキャッキャ....orz $requestData = $this->request->input('json_encode', true); } }
(°ω°;.....!
できました。
HTTPメソッドをPUT
とか
ContentType
とか指定しなくても
自分でphp://input
を指定しなくても
いけました。
(´;ω;`)ブワッ
読み飛ばしてた箇所の説明
リクエストメソッドがPUTで、ContentType
を指定したところのparse_str()
<?php ... $data = $this->_readInput(); // ここでリクエストを取得 parse_str($data, $this->data); // ? なにこれ?
application/x-www-form-urlencoded
で明記されたルールのリクエストだと受け取ることができ、$this->request->data
へ書き込む処理だった。
application/x-www-form-urlencoded ‐ 通信用語の基礎知識
今回はjson
のデータだったのでうまくparseすることができなかった。
$_POST
が宣言されない謎
調べたけど、ようわからん。。。
まとめ
ドキュメントは一度は目を通すべし。