CakePHPでgroup by構文を使うこのエントリをはてなブックマークに追加

12 月 14, 2008

CakePHPのmodelでgroup by使おうとしたのだけども、findAllじゃどうやらできないらしいのでソースをちらほら読んでたら機能はあるみたいなので試してみた(そりゃあるよな
Cakeのバージョンはcake_1.2.0.7692-rc3でございます。ちょっと古いな。

とりあえず↑にも書いたけど、findAllでやると無理っぽい。ので、findでfindAllのように実装すると細かいところまで設定できるようになってる。

cake/libs/model/model.php
Line.1767~

<?php
function find($conditions = null, $fields = array(), $order = null, $recursive = null) {
  if (!is_string($conditions) || (is_string($conditions) && !array_key_exists($conditions, $this->_findMethods))) {
    $type = 'first';
    $query = array_merge(compact('conditions', 'fields', 'order', 'recursive'), array('limit' => 1));
  } else {
    list($type, $query) = array($conditions, $fields);
  }

  $db =& ConnectionManager::getDataSource($this->useDbConfig);
  $this->findQueryType = $type;
  $this->id = $this->getID();

  $query = array_merge(
      array(
         'conditions' => null, 'fields' => null, 'joins' => array(), 'limit' => null,
         'offset' => null, 'order' => null, 'page' => null, 'group' => null, 'callbacks' => true
      ),
      (array)$query
  );

としていて、$fieldsをarray_mergeしているのがわかる。で、findAllはっつーと

<?php
  function findAll($conditions = null, $fields = null, $order = null, $limit = null, $page = 1, $recursive = null) {
    //trigger_error(__('(Model::findAll) Deprecated, use Model::find("all")', true), E_USER_WARNING);
    return $this->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive'));
  }
?>

としていて、findでallをしてやると全部検索になるっていう。
まぁ今回findAll使わないけど、こんな感じになってるよっていう感じで。

今回のデータ。

create table groups (
 id INT(11) unsigned NOT NULL auto_increment,
 sex enum("male","female") NOT NULL default "male",
 age int(11) unsigned NOT NULL default 0,
 modified datetime,
 created datetime,
 PRIMARY KEY (id)
);

insert into groups (sex, age) values
("male",10),
("female",10),
("female",30),
("female",20),
("male",30),
("male",20),
("female",20),
("male",20),
("female",10),
("male",10);

mysql> select * from groups;
+----+--------+-----+----------+---------+
| id | sex    | age | modified | created |
+----+--------+-----+----------+---------+
|  1 | male   |  10 | NULL     | NULL    |
|  2 | female |  10 | NULL     | NULL    |
|  3 | female |  30 | NULL     | NULL    |
|  4 | female |  20 | NULL     | NULL    |
|  5 | male   |  30 | NULL     | NULL    |
|  6 | male   |  20 | NULL     | NULL    |
|  7 | female |  20 | NULL     | NULL    |
|  8 | male   |  20 | NULL     | NULL    |
|  9 | female |  10 | NULL     | NULL    |
| 10 | male   |  10 | NULL     | NULL    |
+----+--------+-----+----------+---------+
10 rows in set (0.00 sec)
mysql> SELECT  age, count(age) as cnt  FROM groups GROUP BY age ORDER BY cnt DESC LIMIT 3 ;
+-----+-----+
| age | cnt |
+-----+-----+
|  10 |   4 |
|  20 |   4 |
|  30 |   2 |
+-----+-----+
3 rows in set (0.00 sec)

適当に。↑のクエリを発行したいとします。

先ほどのプログラムも見たとおりだけど、array_mergeの中に、groupというのがあるので、これに値を渡せばよいことがわかります。

ってことで、こんな感じにfindのfiledsにあたる部分の配列を作成します。

<?php
  $fields = array(
                    "conditions" => array(),
                    "fields" => "age, count(Group.age) as cnt",
                    "limit" => 3,
                    "group" => "age",
                    "order" => "ORDER BY cnt DESC",
                  );
  $result = $this->Group->find("all", $fields);
?>

まー見ればわかると思うけど、filedsの中身は、array_mergeでマージされるようにパラメータをうまく調整しただけです。
ちなみに単品テーブルで、アソシエーションはなしです。

これの結果が以下。

Array
(
    [0] => Array
      (
      [Group] => Array([age] => 10)
      [0] => Array([cnt] => 4)
      )
    [1] => Array
      (
      [Group] => Array([age] => 20)
      [0] => Array([cnt] => 4)
      )
    [2] => Array
      (
      [Group] => Array([age] => 30)
      [0] => Array([cnt] => 2)
      )
)

こんな感じ。
知らなかったのですが、モデル名にあたる部分(この場合だとage)はGroupの配列はいってくるけど、それ以外は素の配列で戻ってくるのね。ちょっと手間だな。
「count(Group.age) as cnt」の部分を「count(Group.age) as Group.cnt」っていうこともしてみたけど、SQLエラーになった\(^o^)/そりゃそうだけどって感じだけども。

実践で使うなら、modelに書いてデータを整理しなおしてから返したほうがよさそうね。

Categories: cakephp
Tags:

1 件のコメント »

このコメント欄の RSS フィードトラックバック URL

  1. good site!

    コメント by car insurance — 2009 年 2 月 7 日 @ 6:33 PM

コメントをどうぞ