[cakePHP]findの戻り値からモデル名を除去する

cakephpでModel->find(‘all’); をつかうと、検索結果を取得することができる。
この結果を

$this->set('results',$this->Model->find('all'));
$this->set('status','ok');

という具合にパラメタにセットして、

 protected function return_as_json(){
$this->header('Content-Type: text/html');
echo $this->indent( json_encode($this->viewVars) );
exit();
}

というような関数を呼び出してあげれば、検索結果をJSONで返すWebAPI的なものを作ることができる。

ところがこれがちょっと変で。JavaScriptからみた戻り値はこんなふうに見える。

{
"results": [
{
"Model": {
"id": "5",
"user_id": "8",
   :(略)
}
},
{
"Model": {
"id": "4",
"user_id": "8",
   :(略)
}
},
],
"status": "ok"
}

普通APIで検索結果を取得する時って、普通こうなってません?

{
"results": [
{
"id": "5",
"user_id": "8",
   :(略)
},
{
"id": "4",
"user_id": "8",
   :(略)
},
],
"status": "ok"
}

呼び出し側のJavaScriptから見たら、

function(res){//success
var models = res['results'];
for (var i = 0; i < models.length; i++) {
console.log(models[i]['Model'].id)
}
}

って謎の型名をつけるよりも、

function(res){//success
var models = res['results'];
for (var i = 0; i < models.length; i++) {
console.log(models[i].id)
}
}

のほうが自然ですよね。

前置きが長くなったけれど、そういうわけで型名を付けないで検索結果を返すようにする実装。
本当はjson_encodeするときに型名を落とすのが適切だと思うのだけれど、うまいやり方を思いつかなかったので、modelのカスタムfindとして定義してみた。

連想配列のまま構造を変えるのではなくて、一旦フラットにしてテキストとしてモデル名を除去しています。

allの代わりにnoModelNameを使えば、モデル名部分のないハッシュを得ることができます。

 $this->set('results', $this->Model->find('noModelName') );
$this->return_as_json();

[JavaScript]変数の存在確認

・同じ比較方法の場合、if文よりも三項演算子の方がやや速い
・もっとも高速な方法は論理和を使う方法
・もっとも時間がかかる方法は型比較をif文で行う方法
・null比較、型比較で三項演算子を利用すると真偽によって速度が(割と)変わる
( 変数の存在を確認する方法と速度比較(その2) – blog.katsuma.tv)

五年前の記事なのだけれど、関数に渡ってきた引数を存在チェックするとき、どう書くのが早いだろう?という実験レポート。
このレポートによると、

var ret;
if(arg) {
ret = arg;
} else {
ret = 'bar';
}

という比較的普通のコードと比べて

var ret = (arg!=null)? arg : 'bar';

三項演算子を使うと倍近く早いらしい。

でも、変数の初期化だったら僕はこう書くことが多い。

if(!arg) { arg = 'bar';}

ということで、五年経った現状と、僕のコードどうなんだろう、というのを見たいと思ったので試してみた。

Google Chrome 26.0.1410.65

[checkStrTernary]   1786
[checkStrIf]    1786
[checkArgsLogicalAdd]    49
[checkArgsTernary]   45
[checkArgsIf]   51
[checkArgsIf2]  48
[checkNullTernary]  74
[checkNullIf]   59
[checkTypeTernary]  61
[checkTypeIf]   48

待てw

やるんじゃないかとは思っていましたが、実質的に何もしていないことを見ぬいて最適化された結果、Date()の分解能テストと化しています。たぶんループが丸ごと削除されていますね。
もちろん、checkStrTernaryとcheckStrIf以外の順位は実施のたびに入れ替わりますので、個別のコードの有利不利を判定することはできません。

FireFox 17.0.1

[checkStrTernary]   14555
[checkStrIf]    14263
[checkArgsLogicalAdd]    10572
[checkArgsTernary]   10674
[checkArgsIf]   11215
[checkArgsIf2]  11258
[checkNullTernary]  10659
[checkNullIf]   10750
[checkTypeTernary]  11102
[checkTypeIf]   11714

うむ。さすが高機能JavaScript開発環境(僕命名)。
これを見ていると、やっぱりcheckStrTernaryとcheckStrIfが遅いものの、あとはそれほど大きな差じゃない程度になっています。checkArgsIfとcheckArgsIf2などは実施のたびに早いほうが入れ替わるので、誤差の範囲内です。

InternetExplorer 9.0.8112.16421

ログ: [checkStrTernary]   11868
ログ: [checkStrIf]    11766
ログ: [checkArgsLogicalAdd]    8618
ログ: [checkArgsTernary]   8525
ログ: [checkArgsIf]   10521
ログ: [checkArgsIf2]  10767
ログ: [checkNullTernary]  10777
ログ: [checkNullIf]   10655
ログ: [checkTypeTernary]  11507
ログ: [checkTypeIf]   10584

おおむねFireFoxと同じ傾向が出ています。よほど変な書き方をしない限り大差ないということですね。

所感

近年のブラウザにおいては、最適化が進んだ結果、以前ほど三項演算子有利ではなくなってきました。
それでも1割程度の差がつくので、シビアな場面ではトライする価値があるかもしれないですが、普通にコード書くときは、読みやすいほうを使うのでいいんじゃないかなーと思いました。

コード: