※前提として、yield を使うので PHP5.5 以上であることが必要です
Phalcon の Resultset (Model::findの返却値) は多少の機能はありますが、ほぼORM機能を持ったクエリ結果カーソルの域を出ないので Ginq を使っています。
Resultset → Ginq
変換するには Ginq::fromLazy と yield を使います。
$rs = BlogEntries::find();
$entries = Ginq::fromLazy(function() use (&$rs) {
foreach ($rs as $item) {
yield $item;
}
});
// 一日の中で PV 数が最上位のブログ記事を取得
$entries = $entries
->groupBy(['e'=>'e.getDate()'])
->select(function(GroupingGinq $g) {
return $g->max(['e'=>'e.getPV()']);
});
// 出力(これを View でやる)
foreach ($entries as $date => $entry) {
echo $date . ":" . $entry->getTitle();
}
本来 Resultset はイテレータなので、そのまま Ginq::from(BlogEntries::find())
と書いても動くはずなのですが、なぜか要素がところどころ抜け落ちたりしました。
原因は不明ですが yield を使うことで極限まで抽象化できるので Ginq::fromLazy で解決できます。
find の返却値自体を Ginq にしちゃう
継承する中間クラスとして ModelBase クラスを定義し、find メソッドをオーバーライドします。
use Ginq\Ginq;
use Phalcon\Mvc\Model;
class ModelBase extends Model {
/**
* @param array $parameters
* @return static[]|Ginq
*/
public static function find($parameters = null) {
$rs = parent::find($parameters);
return Ginq::fromLazy(function() use (&$rs) {
foreach ($rs as $item) {
yield $item;
}
});
}
}
すべての Model で ModelBase を継承するだけで、find の結果が Ginq に統一されます。
思い切ったやりかたではありますが、中途半端に Ginq を使い分けるよりもコーディングポリシーを統一できるので、私は実際のプロダクトを一気にリファクタリングしました。
注意が必要なところ
いまのところ、この方法を使う上で以下の点に気を付ける必要があります。
count($entries) ではなく $entries->count()
count($entries)
では正しく数量が計算できませんでした。
Ginq の count メソッドを使って $entries->count()
と記述する必要があります。
Volt テンプレートエンジンの for 文の特殊変数が機能しない
Volt のループ構文ではループコンテキストという特殊変数が使えますが、このうち
loop.first
loop.last
loop.length
などは使用できませんでした。もちろん $entries->toList()
でプリミティブの配列にすれば使えます。
{% for entry in entries.toList() %}
{{ loop.last }}
{% endfor %}
このへんは責任が持てませんので、各自で検証してください。
暇があったらページネータの GinqAdapter を書こうと思ってます。