2008年3月2日日曜日

Cakephp1.2 特定のモデルのPaginate SQLをカスタマイズする

CakePHP1.2になって超絶便利になったController::paginate。
$this->paginate = array('limit' => 10, 'page' => 1);
$this->paginate(MODEL_NAME, Mixed);


こんな一行で勝手に10データ分ずつページネーションやってくれる優れもの。
大変便利な機能なんですけど、一つ欠点があります。

例えば、

IDuser_idlogin_date
112008-02-29
212008-03-01
322008-02-29
432008-02-29


こんな「logs」なんつーテーブルがあったとします。
ここで、user_idごとにlogin回数を集計したいとなった場合、GROUP BY 句を使う必要があります。
そのときにController::paginateメソッドではうまくページネーションすることができなくなってしまうというわけです。
うーむ、ここばっかりは自前でページネーション作るしかないかぁ。がっくし、と少し肩を落としながら該当部分のソースをごろごろ眺めていると、

cake/libs/controller/controller.php : LINE 957
if (method_exists($object, 'paginateCount')) {
$count = $object->paginateCount($conditions, $recursive);
} else {
$count = $object->findCount($conditions, $recursive);
}
$pageCount = intval(ceil($count / $limit));

となってました。
どうやらモデル内にpaginateCountというメソッドを用意してあげて、そいつが適切なカウント数を返してあげればGROUP BY句が入っていようが、きちんとページネーションしてくれるというわけでした!

モデル名はLogということにして試しに書いてみるとこんな感じ。
app/model/log.php
class LogModel extends AppModel {
var $name = "Log";
function paginateCount($conditions, $recursive) {
$result = $this->query('SELECT Log.user_id FROM logs as Log WHERE ' . $conditions[1]);
return count($result);
}
}

今回の検証ではconditionsは配列になっていて、[1]にController::paginateに渡したWHERE句が入っていました。
この辺はもうちょっと調べなきゃです。

と、このメソッドをモデルに突っ込んでおくだけで集計後の値でカウントしたいようなテーブルにおいてもpaginateが使用できます。なかなか素敵ですね。

ただ、
return count($result);

がなんかかっこわるいですw

SELECT COUNT ( SELECT Log.user_id FROM logs as Log WHERE HOGE GROUP BY Log.user_id );

とかいけそうな気がしたけど駄目でしたorz
あれー、こういう書き方ってダメなんだっけ。。

4 件のコメント:

Unknown さんのコメント...
このコメントは投稿者によって削除されました。
Unknown さんのコメント...
このコメントは投稿者によって削除されました。
Unknown さんのコメント...

グダグダしてすみません…

やっぱりこれで
$result = $this->find($conditions, 'count(DISTINCT Log.user_id)', null, $recursive);
return $result[0]['count'];

最後の$result[0]がややマジックナンバーになって解りづらいですよねぇ^^;

エガシラリュウジ さんのコメント...

わーい。こんなところでcohtanに助けられたー♪
まじ助かった。

久しぶりにブログを書く

最初ログインできなくて焦った。 いや、ログインはできたのだが、ログイン後に自分のブログの管理画面に遷移できず、新しいブログを作成する画面になってしまったからビビッてしまった。 どうやら、Google+(現在はサービス終了)に紐づいたプロフィールを設定しているとこのような状況に陥る...