From 78ff2ad4bd9f1d5c6d311692041c0a801a84062b Mon Sep 17 00:00:00 2001 From: mark_story Date: Thu, 29 May 2008 12:27:23 +0000 Subject: [PATCH] Implemented 'group' by conditions for model finds. Added tests from 'wluigi' Closes #2833 git-svn-id: https://svn.cakephp.org/repo/branches/1.2.x.x@7057 3807eeeb-6ff5-0310-8944-8be069107fe0 --- cake/libs/model/datasources/dbo_source.php | 30 ++++++++--- cake/libs/model/model.php | 5 +- cake/tests/cases/libs/model/model.test.php | 63 ++++++++++++++++++++-- 3 files changed, 87 insertions(+), 11 deletions(-) diff --git a/cake/libs/model/datasources/dbo_source.php b/cake/libs/model/datasources/dbo_source.php index 2b73e2411..9c9ee8790 100644 --- a/cake/libs/model/datasources/dbo_source.php +++ b/cake/libs/model/datasources/dbo_source.php @@ -1008,7 +1008,8 @@ class DboSource extends DataSource { 'offset' => $queryData['offset'], 'joins' => $queryData['joins'], 'conditions' => $queryData['conditions'], - 'order' => $queryData['order'] + 'order' => $queryData['order'], + 'group' => $queryData['group'] ), $model ); @@ -1028,6 +1029,7 @@ class DboSource extends DataSource { $assocData['offset'] = ($assocData['page'] - 1) * $assocData['limit']; } $assocData['limit'] = $this->limit($assocData['limit'], $assocData['offset']); + switch($type) { case 'hasOne': @@ -1041,7 +1043,8 @@ class DboSource extends DataSource { 'conditions' => $conditions, 'table' => $this->fullTableName($linkModel), 'fields' => $fields, - 'alias' => $alias + 'alias' => $alias, + 'group' => null )); $query = array_merge(array('order' => $assocData['order'], 'limit' => $assocData['limit']), $query); } else { @@ -1073,7 +1076,8 @@ class DboSource extends DataSource { 'table' => $this->fullTableName($linkModel), 'alias' => $alias, 'order' => $assocData['order'], - 'limit' => $assocData['limit'] + 'limit' => $assocData['limit'], + 'group' => null, ); break; case 'hasAndBelongsToMany': @@ -1104,6 +1108,7 @@ class DboSource extends DataSource { 'alias' => $alias, 'fields' => array_merge($this->fields($linkModel, $alias, $assocData['fields']), $joinFields), 'order' => $assocData['order'], + 'group' => null, 'joins' => array(array( 'table' => $joinTbl, 'alias' => $joinAssoc, @@ -1203,7 +1208,8 @@ class DboSource extends DataSource { 'alias' => $this->alias . $this->name($query['alias']), 'order' => $this->order($query['order']), 'limit' => $this->limit($query['limit'], $query['offset']), - 'joins' => join(' ', $query['joins']) + 'joins' => join(' ', $query['joins']), + 'group' => $this->group($query['group']) )); } /** @@ -1229,7 +1235,7 @@ class DboSource extends DataSource { switch (strtolower($type)) { case 'select': - return "SELECT {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$order} {$limit}"; + return "SELECT {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$order} {$limit}{$group}"; break; case 'update': if (!empty($alias)) { @@ -1566,7 +1572,7 @@ class DboSource extends DataSource { * @return array */ function __scrubQueryData($data) { - foreach (array('conditions', 'fields', 'joins', 'order', 'limit', 'offset') as $key) { + foreach (array('conditions', 'fields', 'joins', 'order', 'limit', 'offset', 'group') as $key) { if (!isset($data[$key]) || empty($data[$key])) { $data[$key] = array(); } @@ -1984,6 +1990,18 @@ class DboSource extends DataSource { return ' ORDER BY ' . $keys . $direction; } } +/** + * Create a GROUP BY SQL clause + * + * @param string $group Group By Condition + * @return mixed string condition or null + **/ + function group($group) { + if ($group) { + return ' GROUP BY ' . $this->__quoteFields($group); + } + return null; + } /** * Disconnects database, kills the connection and says the connection is closed, * and if DEBUG is turned on, the log for this object is shown. diff --git a/cake/libs/model/model.php b/cake/libs/model/model.php index 6898e64bd..d6830d071 100644 --- a/cake/libs/model/model.php +++ b/cake/libs/model/model.php @@ -1715,7 +1715,8 @@ class Model extends Overloadable { * 'conditions' => array('name' => 'Thomas Anderson'), * 'fields' => array('name', 'email'), * 'order' => 'field3 DESC', - * 'recursive' => 2)); + * 'recursive' => 2, + * 'group' => 'type')); * * Specifying 'fields' for new-notation 'list': * - If no fields are specified, then 'id' is used for key and 'model->displayField' is used for value. @@ -1745,7 +1746,7 @@ class Model extends Overloadable { $query = array_merge( array( 'conditions' => null, 'fields' => null, 'joins' => array(), - 'limit' => null, 'offset' => null, 'order' => null, 'page' => null + 'limit' => null, 'offset' => null, 'order' => null, 'page' => null, 'group' => null ), $query ); diff --git a/cake/tests/cases/libs/model/model.test.php b/cake/tests/cases/libs/model/model.test.php index 5d3d690fa..6d558cf87 100644 --- a/cake/tests/cases/libs/model/model.test.php +++ b/cake/tests/cases/libs/model/model.test.php @@ -49,7 +49,7 @@ class ModelTest extends CakeTestCase { 'core.document', 'core.device', 'core.document_directory', 'core.primary_model', 'core.secondary_model', 'core.something', 'core.something_else', 'core.join_thing', 'core.join_a', 'core.join_b', 'core.join_c', 'core.join_a_b', 'core.join_a_c', 'core.uuid', 'core.data_test', 'core.posts_tag', 'core.the_paper_monkies', 'core.person', 'core.underscore_field', - 'core.node', 'core.dependency' + 'core.node', 'core.dependency', 'core.product' ); function start() { @@ -167,7 +167,8 @@ class ModelTest extends CakeTestCase { ) )), 'conditions' => array(), - 'order' => null + 'order' => null, + 'group' => null ), $Article ); @@ -425,8 +426,64 @@ class ModelTest extends CakeTestCase { 'Mother' => array(), 'Father' => array()))); $this->assertEqual($result, $expected); - } + } + + function testGroupByFind() { + $this->loadFixtures('Product'); + $Product =& new Product(); + $result = $Product->find('all',array('fields'=>array('Product.type','MIN(Product.price) as price'), 'group'=> 'Product.type')); + + $expected = array( + 0 => array( + 'Product' => array( + 'type' => 'Clothing', + 'price' => 32) + ), + 1 => array( + 'Product' => array( + 'type' => 'Food', + 'price' => 9) + ), + 2 => array( + 'Product' => array( + 'type' => 'Music', + 'price' => 4) + ), + 3 => array( + 'Product' => array( + 'type' => 'Toy', + 'price' => 3) + ), + ); + + $this->assertEqual($result, $expected); + } + + function testGroupByFindAssociations() { + $this->loadFixtures('Project', 'Thread', 'Message', 'Bid'); + $Thread =& new Thread(); + $result = $Thread->find('all', array('conditions' => array('Thread.project_id' => 1 ), 'group' => 'Thread.project_id')); + $expected = array( + array( + 'Thread' => array( + 'id' => '1', + 'project_id' => 1, + 'name' => 'Project 1, Thread 1', + ), + 'Message' => array( + array( 'id' => 1, + 'thread_id' => 1, + 'name' => 'Thread 1, Message 1' + ) + + ) + ), + ); + $this->assertEqual($result, $expected); + + } + function testIdentity() { $TestModel =& new Test(); $result = $TestModel->alias;