diff --git a/src/Storm/Model/Loader.php b/src/Storm/Model/Loader.php index ead8016d25774d4a78aa94fee64fe492e569cfe9..61352097fee9c0e6202bdff8fdb6848fda6671d6 100644 --- a/src/Storm/Model/Loader.php +++ b/src/Storm/Model/Loader.php @@ -397,6 +397,9 @@ class Storm_Model_Loader { * */ public function findAllBy($args) { + if ($args instanceof Storm_Query) + return $this->getPersistenceStrategy()->findAllBy($args); + $filter_given_func = isset($args['callback']) ? $args['callback'] : null; $filter_func = null; @@ -452,25 +455,38 @@ class Storm_Model_Loader { } - /** * @param array $args * @return Storm_Model_Abstract */ public function findFirstBy($args) { - $args['limit'] = 1; - $instances = $this->findAllBy($args); + $instances = $this->findAllBy($this->_prepareFirstBy($args)); - if (count($instances) == 0) { + if (count($instances) == 0) return null; - } + $first_instance = reset($instances); $this->cacheInstance($first_instance); return $first_instance; } + protected function _prepareFirstBy($args) { + if ($args instanceof Storm_Query) + return $args->limit(1); + + $args['limit'] = 1; + return $args; + } + + public function fetchAllBy($fields, $params) { return $this->getPersistenceStrategy()->fetchAllBy($fields, $params); } + + + public function greater(string $key, + string $value) : Storm_Model_PersistenceStrategy_Clause { + return Storm_Model_PersistenceStrategy_Clause::greater($key, $value); + } } diff --git a/src/Storm/Model/PersistenceStrategy/Clause.php b/src/Storm/Model/PersistenceStrategy/Clause.php index a1c6025eb80253716197bb7caf12815a8cc4ced0..b41320c55089c9e1989c3daf3f1e55531f562dce 100644 --- a/src/Storm/Model/PersistenceStrategy/Clause.php +++ b/src/Storm/Model/PersistenceStrategy/Clause.php @@ -48,6 +48,7 @@ class Storm_Model_PersistenceStrategy_Clause { CLAUSE_LIMIT = 'limit', CLAUSE_LIMIT_PAGE = 'limitPage', CLAUSE_ORDER_BY = 'order by', + CLAUSE_GROUP_BY = 'group by', PERCENT = '%'; @@ -88,6 +89,8 @@ class Storm_Model_PersistenceStrategy_Clause { return (new Storm_Model_PersistenceStrategy_ClauseLimitPage($key, $operator, $value_or_array)); if (static::CLAUSE_ORDER_BY === $operator) return (new Storm_Model_PersistenceStrategy_ClauseOrderBy($key, $operator, $value_or_array)); + if (static::CLAUSE_GROUP_BY === $operator) + return (new Storm_Model_PersistenceStrategy_ClauseGroupBy($key, $operator, $value_or_array)); return (new static($key, $operator, $value_or_array)); } @@ -196,6 +199,11 @@ class Storm_Model_PersistenceStrategy_Clause { } + public static function group(string $value) : self { + return static::newFor(static::CLAUSE_GROUP_BY, static::CLAUSE_GROUP_BY, $value); + } + + public static function order(string $key, ?Storm_Model_PersistenceStrategy_Clause $clause = null) : self { return static::newFor($key, static::CLAUSE_ORDER_BY, $clause); } @@ -548,6 +556,28 @@ class Storm_Model_PersistenceStrategy_ClauseLimitPage +class Storm_Model_PersistenceStrategy_ClauseGroupBy + extends Storm_Model_PersistenceStrategy_Clause { + + public function assemble($select) : self { + $select->group($this->getValueOrArray()); + return $this; + } + + + public function getFormatDb($table) : string { + return ''; + } + + + public function containAttibuteInVolatile(array $model) : bool { + return true; + } +} + + + + class Storm_Model_PersistenceStrategy_ClauseOrderBy extends Storm_Model_PersistenceStrategy_Clause { diff --git a/src/Storm/Model/PersistenceStrategy/Db.php b/src/Storm/Model/PersistenceStrategy/Db.php index c5157b57bca4c9ec2d1776f004f03e223d9a730b..ed70a15d2504ea309c69cdae59f3dbc6941d9c23 100644 --- a/src/Storm/Model/PersistenceStrategy/Db.php +++ b/src/Storm/Model/PersistenceStrategy/Db.php @@ -172,7 +172,7 @@ class Storm_Model_PersistenceStrategy_Db */ public function _generateSelectFor($args) { if ($args instanceof Storm_Query) - return $args->assembleFor($this->getTable()->select()); + return $args->getSelectAndAssemble(); if (array_key_exists('role', $args) && array_key_exists('model', $args)) { $model = $args['model']; diff --git a/src/Storm/Model/PersistenceStrategy/Volatile.php b/src/Storm/Model/PersistenceStrategy/Volatile.php index 7abb84075309d1a1d085dacfb1250aa6bd9f05fc..afdd2b73c2120ae55b109503a5d421fbb427718c 100644 --- a/src/Storm/Model/PersistenceStrategy/Volatile.php +++ b/src/Storm/Model/PersistenceStrategy/Volatile.php @@ -24,13 +24,14 @@ THE SOFTWARE. */ -class Storm_Model_PersistenceStrategy_Volatile extends Storm_Model_PersistenceStrategy_Abstract { +class Storm_Model_PersistenceStrategy_Volatile + extends Storm_Model_PersistenceStrategy_Abstract { + protected $_instances = [], $desc_order = false, $_table; - public function getTable() { return $this->_table; } @@ -52,7 +53,7 @@ class Storm_Model_PersistenceStrategy_Volatile extends Storm_Model_PersistenceS $limit = $filtered->getLimit(); $limit_page = $filtered->getLimitPage(); - $page_size=0; + $page_size = 0; if ($limit_page) { list($page, $page_size) = $limit_page; if ($page > 0) $page -= 1; diff --git a/src/Storm/Model/PersistenceStrategy/Volatile/Filtered.php b/src/Storm/Model/PersistenceStrategy/Volatile/Filtered.php index 0baff2c0cd71bd064e26e17740b47bf47dcc0f3b..2f0da257a0ebf2e9a116cff4da9d4d43c5a095fc 100644 --- a/src/Storm/Model/PersistenceStrategy/Volatile/Filtered.php +++ b/src/Storm/Model/PersistenceStrategy/Volatile/Filtered.php @@ -47,11 +47,12 @@ class Storm_Model_PersistenceStrategy_Volatile_Filtered { } - public function extractRoleAndModel(Storm_Model_Loader $loader) : self { + public function extractRoleAndModel(Storm_Model_Loader $loader) { if ($this->_query) return $this; - if (array_key_exists('role', $this->_select) && array_key_exists('model', $this->_select)) { + if (array_key_exists('role', $this->_select) + && array_key_exists('model', $this->_select)) { $model = $this->_select['model']; $role = $this->_select['role']; unset($this->_select['model']); @@ -65,7 +66,8 @@ class Storm_Model_PersistenceStrategy_Volatile_Filtered { } if (array_key_exists('scope', $this->_select)) { - $this->_select = array_merge($this->_select, array_change_key_case($this->_select['scope'])); + $this->_select = array_merge($this->_select, + array_change_key_case($this->_select['scope'])); unset($this->_select['scope']); } @@ -111,8 +113,8 @@ class Storm_Model_PersistenceStrategy_Volatile_Filtered { public function getGroupBy() : string { - if (!$this->_select) - return ''; + if ($this->_query) + return $this->_query->getGroupByValue(); $group_by = $this->_select['group_by'] ?? ''; unset($this->_select['group_by']); diff --git a/src/Storm/Query.php b/src/Storm/Query.php index c116a582e1ce2720365fbdc1591774a26d310baa..207e2110c0b32a7f8d15f77c1c5773cb228f9f26 100644 --- a/src/Storm/Query.php +++ b/src/Storm/Query.php @@ -33,6 +33,7 @@ class Storm_Query { protected Storm_Model_Loader $_loader; protected ?Storm_Model_PersistenceStrategy_Clause $_clause_limit; protected ?Storm_Model_PersistenceStrategy_Clause $_clause_limit_page; + protected ?Storm_Model_PersistenceStrategy_Clause $_clause_group_by; public function __construct() { $this->_clauses = []; @@ -40,6 +41,7 @@ class Storm_Query { $this->_terms = []; $this->_clause_limit = null; $this->_clause_limit_page = null; + $this->_clause_group_by = null; } @@ -61,7 +63,9 @@ class Storm_Query { } - public function assembleFor($select) { + public function getSelectAndAssemble() { + $select = $this->_loader->getTable()->select(); + foreach ($this->getClauses() as $clause) $clause->assemble($select); @@ -74,6 +78,9 @@ class Storm_Query { if ($this->_clause_limit_page) $this->_clause_limit_page->assemble($select); + if ($this->_clause_group_by) + $this->_clause_group_by->assemble($select); + return $select; } @@ -100,12 +107,23 @@ class Storm_Query { public function getLimitValue() { - return $this->_clause_limit ? $this->_clause_limit->getValueOrArray() : ''; + return $this->_clause_limit ? + $this->_clause_limit->getValueOrArray() + : ''; } public function getLimitPageValue() : ?array { - return $this->_clause_limit_page ? $this->_clause_limit_page->getValueOrArray() : null; + return $this->_clause_limit_page + ? $this->_clause_limit_page->getValueOrArray() + : null; + } + + + public function getGroupByValue() : string { + return $this->_clause_group_by + ? $this->_clause_group_by->getValueOrArray() + : ''; } @@ -121,6 +139,12 @@ class Storm_Query { } + protected function _group(string $value) : self { + $this->_clause_group_by = Storm_Model_PersistenceStrategy_Clause::group($value); + return $this; + } + + protected function _eq(string $key, string $value) : self { $this->_clauses [] = Storm_Model_PersistenceStrategy_Clause::equal($key, $value); return $this; @@ -287,21 +311,21 @@ class Storm_Query { protected function _fetchAll() : array { return $this->_loader - ? $this->_loader->getPersistenceStrategy()->findAllBy($this) + ? $this->_loader->findAllBy($this) : []; } protected function _count() : array { return $this->_loader - ? $this->_loader->getPersistenceStrategy()->countBy($this) + ? $this->_loader->countBy($this) : []; } - protected function _fetchFirst() : Storm_Model_Abstract { + protected function _fetchFirst() : ?Storm_Model_Abstract { return $this->_loader - ? $this->_loader->getPersistenceStrategy()->findFirstBy($this) + ? $this->_loader->findFirstBy($this) : null; } } diff --git a/tests/Storm/Test/LoaderTest.php b/tests/Storm/Test/LoaderTest.php index d30618935d4a0dcf4d1768d747ddd92e2ea6e647..f3b68db384ee10bfa498066ee560b3b60060395c 100644 --- a/tests/Storm/Test/LoaderTest.php +++ b/tests/Storm/Test/LoaderTest.php @@ -729,3 +729,33 @@ class Storm_Test_LoaderQueryOrderTest extends Storm_Test_LoaderTestCase { $this->_select->getAttributesForLastCallOn('order')); } } + + + + +class Storm_Test_LoaderQueryGroupByTest extends Storm_Test_LoaderTestCase { + + protected $_select; + + public function setUp() { + parent::setUp(); + + $this->_table + ->whenCalled('select')->answers($this->_select = $this->mock()) + ->whenCalled('fetchAll')->with($this->_select) + ->answers(new Zend_Db_Table_Rowset([])); + + $this->_select->whenCalled('group')->answers($this->_select); + } + + + /** @test */ + public function withGroupByLoaderShouldCallGroupOnDbSelect() { + Storm_Query::group('name') + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['name'], + $this->_select->getAttributesForLastCallOn('group')); + } +} diff --git a/tests/Storm/Test/LoaderVolatileTest.php b/tests/Storm/Test/LoaderVolatileTest.php index 09c2aa8f46d9cb52ed25a8338328303e6793f048..4dcd13c1a05b1e710f0a212a219befc02477f06e 100644 --- a/tests/Storm/Test/LoaderVolatileTest.php +++ b/tests/Storm/Test/LoaderVolatileTest.php @@ -1155,4 +1155,15 @@ class Storm_Test_LoaderVolatileClauseOrderTest extends Storm_Test_ModelTestCase ->collect('login') ->getArrayCopy()); } + + + /** @test */ + public function selectUsersGroupByOrder3ShouldAnswersUserAdminAndUserInvite() { + $this->assertEquals([0 => 'user_admin', 2 => 'user_invite'], + (new Storm_Model_Collection(Storm_Query::from(Storm_Test_VolatileUser::getLoader()) + ->group('order3') + ->fetchAll())) + ->collect('login') + ->getArrayCopy()); + } }