Web Api作成に持ってこい!CakePHPのformatResults
こんにちは。SGKです。
最近新しいコンテンツの作成で Vue のバックエンドとして Cakephp で Web Api をゴリゴリ作成しました。
※ちなみに Cake のバージョンは Cakephp3 です。
Web Apiの作成に当たって今まで使っていなかったけどコレがあってよかったな~となったのが formatResults です。
formatResults は Query(Cake\ORM\Query)のメソッドで、Query の結果を配列やJsonにエンコードなどでシリアライズする際に値を書き換える機能を持っています。
例えば selectしたフィールドや contain したテーブルを結果から除外したり、Entityに定義した仮想プロパティーを加えたりできます。
select したフィールドや contain したテーブルを結果から除外
以下のように setHidden を使って結果から除外する事ができます。
$query
->select(['name', 'subtext'])
->contain(['Infos'])
->where(['info.cost > 100'])
->formatResults(function ($ResultSet) {
return $ResultSet->map(function ($Entity) {
if( empty($Entity) ) return $Entity;
$Entity->setHidden(['subtext', 'info']);
return $Entity;
});
});
print json_encode($query);
//put --> [{"name":"みかん"},{"name":"ぶどう"},...]
このコードでは例として subtext を除外していますが、それなら subtext は select しなくて良いですね、、
※このケースが有効になる例は次の例で示します。
逆に contain した info テーブルの結果を除外する例は効果的で、
「info.cost > 100」の条件を適用するためには info テーブルとの結合は不可欠、しかし結果には必要ない場合はこのように除外できます。
Entityに定義した仮想プロパティーを加える
setVirtual を使うと Entity に定義した仮想プロパティー(バーチャルフィールド)を結果に加えることができます。
$query
->select(['name', 'subtext'])
->contain(['Infos'])
->formatResults(function ($ResultSet) {
return $ResultSet->map(function ($Entity) {
if( empty($Entity) ) return $Entity;
$Entity->setVirtual(['subtext_name']);
$Entity->setHidden(['name', 'subtext']);
return $Entity;
});
});
print json_encode($query);
//put --> [{"subtext_name":"みかん - 蒲郡産中玉"},{"subtext_name":"ぶどう - 大府産ゴルビー"},...]
Entity\Fruits.php
class Fruit extends Entity{
~
protected function _getSubtextName(){
return $this->name . ' - ' . $this->subtext;
}
}
通常、jsonエンコードなどのシリアライズ結果には仮想プロパティーは出力されません。
エンティティクラスの $_virtual の定義を行うことにより可能ではあるのですが、実行するクエリによって出力しないケースも出てくるためこの方法を使います。
この例では結果出力時に App\Model\Entity\Fruit の _getSubtextName を呼び出し、セレクトした name と subtext を文字列結合した結果を作成し、加えて単体では必要ない name と subtext を setHidden で除外しています。
※前提が抜けていますが、、 $query は App\Model\Table\FruitTable から ->find() にて生成されたインスタンスと思ってください。
contain したデータのキー名を変更する
力技な例ですがコレが意外と役立ちました。
この処理が必要な場合として関連データを取得する場合に、テーブル名と出力したいキー名が違う場合です。
例えば関連して取得するテーブルの名前が Infos の場合、このテーブルを contain してそのまま json エンコードすると Infos に属するデータは info というキーで出力されます。
そうではなく Infos から取得したデータを attr というキー名で出力する場合は以下のようになります。
$query
->select(['name'])
->contain(['Infos'])
->formatResults(function ($ResultSet) {
return $ResultSet->map(function ($Entity) {
if( empty($Entity) ) return $Entity;
$Entity->attr = $Entity->info;
$Entity->setHidden(['info']);
return $Entity;
});
});
print json_encode($query);
//put --> [{"name":"みかん", "attr":{"id":1, "cost":300}}, {"name":"ぶどう", "attr":{"id":2, "cost":1500}}, ...]
最後に
Cakephp で formatResults を使って Entity の setVirtual、setHidden、などで出力結果を操作する内容を紹介しました。
ちなみにcakeのソースを眺めたりすると ->find(‘list’) が formatResults を使って実装されている事などが読めたりします。
あと気を付けたい点としては formatResults を使うと仮想プロパティー相当の事が出来てしまいますが、仮想プロパティーで出来ることはそちらでやった方が汎用性があるのでそれが良いです。