diff --git a/src/Storm/Model/Loader.php b/src/Storm/Model/Loader.php index 0d7ba1ddf118726444a5cef3015dd232dacb3020..ead8016d25774d4a78aa94fee64fe492e569cfe9 100644 --- a/src/Storm/Model/Loader.php +++ b/src/Storm/Model/Loader.php @@ -90,7 +90,6 @@ class Storm_Model_Loader { } - public static function resetCache() { static::$_loader_cache = null; } @@ -474,77 +473,4 @@ class Storm_Model_Loader { public function fetchAllBy($fields, $params) { return $this->getPersistenceStrategy()->fetchAllBy($fields, $params); } - - - public function clauseLike(string $key, string $value) : Storm_Model_PersistenceStrategy_Clause { - return Storm_Model_PersistenceStrategy_Clause::clauseLike($key, $value); - } - - - public function clauseNotLike(string $key, string $value) : Storm_Model_PersistenceStrategy_Clause { - return Storm_Model_PersistenceStrategy_Clause::clauseNotLike($key, $value); - } - - - public function clauseGreater(string $key, string $value) : Storm_Model_PersistenceStrategy_Clause { - return Storm_Model_PersistenceStrategy_Clause::clauseGreater($key, $value); - } - - - public function clauseGreaterEqual(string $key, string $value) : Storm_Model_PersistenceStrategy_Clause { - return Storm_Model_PersistenceStrategy_Clause::clauseGreaterEqual($key, $value); - } - - - public function clauseLesser(string $key, string $value) : Storm_Model_PersistenceStrategy_Clause { - return Storm_Model_PersistenceStrategy_Clause::clauseLesser($key, $value); - } - - - public function clauseLesserEqual(string $key, string $value) : Storm_Model_PersistenceStrategy_Clause { - return Storm_Model_PersistenceStrategy_Clause::clauseLesserEqual($key, $value); - } - - - public function clauseIs(string $key) : Storm_Model_PersistenceStrategy_Clause { - return Storm_Model_PersistenceStrategy_Clause::clauseIs($key); - } - - - public function clauseNotIs(string $key) : Storm_Model_PersistenceStrategy_Clause { - return Storm_Model_PersistenceStrategy_Clause::clauseNotIs($key); - } - - - public function clauseIn(string $key, array $array) : Storm_Model_PersistenceStrategy_Clause { - return Storm_Model_PersistenceStrategy_Clause::clauseIn($key, $array); - } - - - public function clauseNotIn(string $key, array $array) : Storm_Model_PersistenceStrategy_Clause { - return Storm_Model_PersistenceStrategy_Clause::clauseNotIn($key, $array); - } - - - public function clauseEqual(string $key, - string $value) : Storm_Model_PersistenceStrategy_Clause { - return Storm_Model_PersistenceStrategy_Clause::clauseEqual($key, $value); - } - - - public function clauseNotEqual(string $key, string $value) : Storm_Model_PersistenceStrategy_Clause { - return Storm_Model_PersistenceStrategy_Clause::clauseNotEqual($key, $value); - } - - - public function clauseStart(string $key, - string $value) : Storm_Model_PersistenceStrategy_Clause { - return Storm_Model_PersistenceStrategy_Clause::clauseStart($key, $value); - } - - - public function clauseEnd(string $key, - string $value) : Storm_Model_PersistenceStrategy_Clause { - return Storm_Model_PersistenceStrategy_Clause::clauseEnd($key, $value); - } } diff --git a/src/Storm/Model/PersistenceStrategy/Clause.php b/src/Storm/Model/PersistenceStrategy/Clause.php index b1db63ec7e60f8cde186de2f3f47de72bfce3f7d..a1c6025eb80253716197bb7caf12815a8cc4ced0 100644 --- a/src/Storm/Model/PersistenceStrategy/Clause.php +++ b/src/Storm/Model/PersistenceStrategy/Clause.php @@ -24,17 +24,17 @@ THE SOFTWARE. */ + class Storm_Model_PersistenceStrategy_Clause { - protected - $_key, - $_operator, - $_value, - $_array, - $_negated, - $_is_array; + protected string $_key; + protected string $_operator; + protected bool $_negated; + protected $_value_or_array; const + CLAUSE_OR = 'or', + CLAUSE_AND = 'and', CLAUSE_WHERE = 'where', CLAUSE_LIKE = 'like', CLAUSE_EQUAL = '=', @@ -44,6 +44,10 @@ class Storm_Model_PersistenceStrategy_Clause { CLAUSE_GREATER_EQUAL = '>=', CLAUSE_LESSER = '<', CLAUSE_LESSER_EQUAL = '<=', + CLAUSE_MATCH = 'MATCH', + CLAUSE_LIMIT = 'limit', + CLAUSE_LIMIT_PAGE = 'limitPage', + CLAUSE_ORDER_BY = 'order by', PERCENT = '%'; @@ -51,184 +55,558 @@ class Storm_Model_PersistenceStrategy_Clause { $this->_negated = false; $this->_key = $key; $this->_operator = $operator; - $this->_is_array = is_array($value_or_array); + $this->_value_or_array = $value_or_array; + } - if ($this->_is_array) - $this->_array = $value_or_array; - if (!$this->_is_array) - $this->_value = $value_or_array; + public static function newFor(string $key, string $operator, $value_or_array) : self { + if (static::CLAUSE_OR === $operator) + return (new Storm_Model_PersistenceStrategy_ClauseOr($key, $operator, $value_or_array)); + if (static::CLAUSE_AND === $operator) + return (new Storm_Model_PersistenceStrategy_ClauseAnd($key, $operator, $value_or_array)); + if (static::CLAUSE_LIKE === $operator) + return (new Storm_Model_PersistenceStrategy_ClauseLike($key, $operator, $value_or_array)); + if (static::CLAUSE_EQUAL === $operator) + return (new Storm_Model_PersistenceStrategy_ClauseEqual($key, $operator, $value_or_array)); + if (static::CLAUSE_IN === $operator) + return (new Storm_Model_PersistenceStrategy_ClauseIn($key, $operator, $value_or_array)); + if (static::CLAUSE_IS === $operator) + return (new Storm_Model_PersistenceStrategy_ClauseIs($key, $operator, $value_or_array)); + if (static::CLAUSE_GREATER === $operator) + return (new Storm_Model_PersistenceStrategy_ClauseGreater($key, $operator, $value_or_array)); + if (static::CLAUSE_GREATER_EQUAL === $operator) + return (new Storm_Model_PersistenceStrategy_ClauseGreaterEqual($key, $operator, $value_or_array)); + if (static::CLAUSE_LESSER === $operator) + return (new Storm_Model_PersistenceStrategy_ClauseLesser($key, $operator, $value_or_array)); + if (static::CLAUSE_LESSER_EQUAL === $operator) + return (new Storm_Model_PersistenceStrategy_ClauseLesserEqual($key, $operator, $value_or_array)); + if (static::CLAUSE_MATCH === $operator) + return (new Storm_Model_PersistenceStrategy_ClauseMatch($key, $operator, $value_or_array)); + if (static::CLAUSE_LIMIT === $operator) + return (new Storm_Model_PersistenceStrategy_ClauseLimit($key, $operator, $value_or_array)); + if (static::CLAUSE_LIMIT_PAGE === $operator) + 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)); + + return (new static($key, $operator, $value_or_array)); } - public static function clauseIs(string $key) : self { - return new static($key, static::CLAUSE_IS, null); - } + public static function newWith(string $key, $value_or_array) : self { + $operator = static::CLAUSE_EQUAL; + if (static::CLAUSE_WHERE === $key) + $operator = static::CLAUSE_WHERE; + + $is_like = ' like' === substr($key, -5); + $key = $is_like ? substr($key, 0, strlen($key) - 5) : $key; + $negated = (' not' === substr($key, -4)); + $key = $negated ? substr($key, 0, strlen($key) - 4) : $key; + + if ($is_like) + $operator = static::CLAUSE_LIKE; + + if (null === $value_or_array) + $operator = static::CLAUSE_IS; + if (is_array($value_or_array)) + $operator = static::CLAUSE_IN; - public static function clauseNotIs(string $key) : self { - return static::clauseIs($key)->_setNegated(true); + return (static::newFor($key, $operator, $value_or_array))->setNegated($negated); } - public static function clauseIn(string $key, array $array) : self { - return new static($key, static::CLAUSE_IN, $array); + public static function is(string $key) : self { + return static::newFor($key, static::CLAUSE_IS, null); } - public static function clauseNotIn(string $key, array $array) : self { - return static::clauseIn($key, $array)->_setNegated(true); + public static function in(string $key, array $array) : self { + return static::newFor($key, static::CLAUSE_IN, $array); } - public static function clauseEqual(string $key, string $value) : self { - return new static($key, static::CLAUSE_EQUAL, $value); + public static function equal(string $key, string $value) : self { + return static::newFor($key, static::CLAUSE_EQUAL, $value); } - public static function clauseNotEqual(string $key, string $value) : self { - return static::clauseEqual($key, $value)->_setNegated(true); + public static function like(string $key, string $value) : self { + return static::newFor($key, static::CLAUSE_LIKE, $value); } - public static function clauseLike(string $key, string $value) : self { - return new static($key, static::CLAUSE_LIKE, $value); + public static function start(string $key, string $value) : self { + return static::newFor($key, static::CLAUSE_LIKE, $value . static::PERCENT); } - public static function clauseNotLike(string $key, string $value) : self { - return static::clauseLike($key, $value)->_setNegated(true); + public static function end(string $key, string $value) : self { + return static::newFor($key, static::CLAUSE_LIKE, static::PERCENT . $value); } - public static function clauseStart(string $key, string $value) : self { - return new static($key, static::CLAUSE_LIKE, $value . static::PERCENT); + public static function greater(string $key, string $value) : self { + return static::newFor($key, static::CLAUSE_GREATER, $value); } - public static function clauseEnd(string $key, string $value) : self { - return new static($key, static::CLAUSE_LIKE, static::PERCENT . $value); + public static function greaterEqual(string $key, string $value) : self { + return static::newFor($key, static::CLAUSE_GREATER_EQUAL, $value); } - public static function clauseGreater(string $key, string $value) : self { - return new static($key, static::CLAUSE_GREATER, $value); + public static function lesser(string $key, string $value) : self { + return static::newFor($key, static::CLAUSE_LESSER, $value); } - public static function clauseGreaterEqual(string $key, string $value) : self { - return new static($key, static::CLAUSE_GREATER_EQUAL, $value); + public static function lesserEqual(string $key, string $value) : self { + return static::newFor($key, static::CLAUSE_LESSER_EQUAL, $value); } - public static function clauseLesser(string $key, string $value) : self { - return new static($key, static::CLAUSE_LESSER, $value); + public static function or(array $array) : self { + return static::newFor(static::CLAUSE_OR, static::CLAUSE_OR, $array); } - public static function clauseLesserEqual(string $key, string $value) : self { - return new static($key, static::CLAUSE_LESSER_EQUAL, $value); + public static function and(array $array) : self { + return static::newFor(static::CLAUSE_AND, static::CLAUSE_AND, $array); } - public static function newWith(string $key, $value_or_array) : self { - $operator = static::CLAUSE_EQUAL; - if (static::CLAUSE_WHERE === $key) - $operator = static::CLAUSE_WHERE; + public static function match(string $key, + array $array, + ?bool $strict = false, + ?bool $boolean_mode = true) : self { + return static::newFor($key, + static::CLAUSE_MATCH, + (new Storm_Model_PersistenceStrategy_Match($array, $strict, $boolean_mode))); + } - $is_like = ' like' === substr($key, -5); - $key = $is_like ? substr($key, 0, strlen($key) - 5) : $key; - $negated = (' not' === substr($key, -4)); - $key = $negated ? substr($key, 0, strlen($key) - 4) : $key; - if ($is_like) - $operator = static::CLAUSE_LIKE; + public static function limit($limit) : self { + return static::newFor(static::CLAUSE_LIMIT, static::CLAUSE_LIMIT, $limit); + } - if (null === $value_or_array) - $operator = static::CLAUSE_IS; - if (is_array($value_or_array)) - $operator = static::CLAUSE_IN; + public static function limit_page(array $range) : self { + return static::newFor(static::CLAUSE_LIMIT_PAGE, static::CLAUSE_LIMIT_PAGE, $range); + } + - return (new static($key, $operator, $value_or_array))->_setNegated($negated); + public static function order(string $key, ?Storm_Model_PersistenceStrategy_Clause $clause = null) : self { + return static::newFor($key, static::CLAUSE_ORDER_BY, $clause); } - protected function _setNegated(bool $negated) : self { + public function setNegated(bool $negated) : self { $this->_negated = $negated; return $this; } - protected function _getValueOrArray() { - return $this->_is_array - ? ($this->_array ?? []) - : ($this->_value ?? ''); + public function getValueOrArray() { + return $this->_value_or_array ?? ''; + } + + + public function assemble($select) : self { + $select->where($this->getFormatDb($select->getTable())); + return $this; } public function getFormatDb($table) : string { if (static::CLAUSE_WHERE === $this->_operator) - return '(' . $this->_getValueOrArray() . ')'; - if (static::CLAUSE_IS === $this->_operator) - return $this->_key . ' ' . $this->_getOperator() . ' null'; + return '(' . $this->getValueOrArray() . ')'; return $table->getAdapter() ->quoteInto($this->_clauseFormatDb(), - $this->_getValueOrArray(), null, null); + $this->getValueOrArray(), null, null); } - protected function _clauseFormatDb() : string { - if (static::CLAUSE_IN === $this->_operator) - return $this->_key . ' ' . $this->_getOperator() . ' (?)'; - if (static::CLAUSE_LIKE === $this->_operator) - return $this->_key . ' ' . $this->_getOperator() . ' ?'; + public function containAttibuteInVolatile(array $model) : bool { + if (!$this->_existKeyInModel($model)) + return $this->_negated; + return ($this->_negated !== ($model[$this->_key] == $this->getValueOrArray())); + } + + + protected function _clauseFormatDb() : string { return $this->_key . $this->_getOperator() . '?'; } protected function _getOperator() : string { - if (static::CLAUSE_EQUAL === $this->_operator) - return ($this->_negated ? '!' : '') . $this->_operator; - if (static::CLAUSE_IS === $this->_operator) - return $this->_operator . ($this->_negated ? ' not' : ''); - return ($this->_negated ? 'not ' : '') . $this->_operator; } - public function containAttibuteInVolatile($model) : bool { - if (!array_key_exists($this->_key, $model)) + protected function _existKeyInModel(array $model) : bool { + return array_key_exists($this->_key, $model); + } +} + + + + +class Storm_Model_PersistenceStrategy_ClauseOr + extends Storm_Model_PersistenceStrategy_Clause { + + public function getValueOrArray() { + return $this->_value_or_array ?? []; + } + + + public function getFormatDb($table) : string { + $request = ''; + $operator = ' ' . $this->_operator . ' '; + foreach ($this->getValueOrArray() as $clause) + $request = implode($operator, array_filter([$request, + $clause->getFormatDb($table)])); + + return '(' . $request . ')'; + } + + + public function containAttibuteInVolatile(array $model) : bool { + foreach ($this->getValueOrArray() as $clause) + if ($clause->containAttibuteInVolatile($model)) + return true; + + return false; + } +} + + + + +class Storm_Model_PersistenceStrategy_ClauseAnd + extends Storm_Model_PersistenceStrategy_ClauseOr { + + public function containAttibuteInVolatile(array $model) : bool { + foreach ($this->getValueOrArray() as $clause) + if (!$clause->containAttibuteInVolatile($model)) + return false; + + return true; + } +} + + + + +class Storm_Model_PersistenceStrategy_ClauseLike + extends Storm_Model_PersistenceStrategy_Clause { + + protected function _clauseFormatDb() : string { + return $this->_key . ' ' . $this->_getOperator() . ' ?'; + } + + + public function containAttibuteInVolatile(array $model) : bool { + if (!$this->_existKeyInModel($model)) + return $this->_negated; + + $matches = preg_match(('/^' . str_replace('%', '.*', + $this->getValueOrArray()) . '$/i'), + $model[$this->_key]); + + return $this->_negated ? !$matches : $matches; + } +} + + + + +class Storm_Model_PersistenceStrategy_ClauseEqual + extends Storm_Model_PersistenceStrategy_Clause { + + protected function _getOperator() : string { + return ($this->_negated ? '!' : '') . $this->_operator; + } +} + + + + +class Storm_Model_PersistenceStrategy_ClauseIn + extends Storm_Model_PersistenceStrategy_Clause { + + public function getValueOrArray() { + return $this->_value_or_array ?? []; + } + + + public function containAttibuteInVolatile(array $model) : bool { + if (!$this->_existKeyInModel($model)) return $this->_negated; - if ($this->_is_array && 0 === count($this->_getValueOrArray())) + if (0 === count($this->getValueOrArray())) throw new Storm_Model_Exception(sprintf('array given for %s is empty', $this->_key)); - if ($this->_is_array && in_array($model[$this->_key], $this->_getValueOrArray())) - return !$this->_negated; + return in_array($model[$this->_key], $this->getValueOrArray()) + ? !$this->_negated + : false; + } + + + protected function _clauseFormatDb() : string { + return $this->_key . ' ' . $this->_getOperator() . ' (?)'; + } +} + + + + +class Storm_Model_PersistenceStrategy_ClauseIs + extends Storm_Model_PersistenceStrategy_Clause { + + public function getValueOrArray() { + return null; + } + + + public function getFormatDb($table) : string { + return $this->_key . ' ' . $this->_getOperator() . ' null'; + } + + + protected function _getOperator() : string { + return $this->_operator . ($this->_negated ? ' not' : ''); + } +} + + + + +class Storm_Model_PersistenceStrategy_ClauseGreater + extends Storm_Model_PersistenceStrategy_Clause { + + public function containAttibuteInVolatile(array $model) : bool { + if (!$this->_existKeyInModel($model)) + return false; + + return $model[$this->_key] > $this->getValueOrArray(); + } +} + + + + +class Storm_Model_PersistenceStrategy_ClauseGreaterEqual + extends Storm_Model_PersistenceStrategy_Clause { + + public function containAttibuteInVolatile(array $model) : bool { + if (!$this->_existKeyInModel($model)) + return false; + + return $model[$this->_key] >= $this->getValueOrArray(); + } +} + + + + +class Storm_Model_PersistenceStrategy_ClauseLesser + extends Storm_Model_PersistenceStrategy_Clause { + + public function containAttibuteInVolatile(array $model) : bool { + if (!$this->_existKeyInModel($model)) + return false; + + return $model[$this->_key] < $this->getValueOrArray(); + } +} + + + - if (static::CLAUSE_LIKE === $this->_operator) { - $matches = preg_match(('/^' . str_replace('%', '.*', - $this->_getValueOrArray()) . '$/i'), - $model[$this->_key]); - return $this->_negated ? !$matches : $matches; - } +class Storm_Model_PersistenceStrategy_ClauseLesserEqual + extends Storm_Model_PersistenceStrategy_Clause { + + public function containAttibuteInVolatile(array $model) : bool { + if (!$this->_existKeyInModel($model)) + return false; + + return $model[$this->_key] <= $this->getValueOrArray(); + } +} + + + + +class Storm_Model_PersistenceStrategy_ClauseMatch + extends Storm_Model_PersistenceStrategy_Clause { + + public function getValueOrArray() { + return $this->_value_or_array ?? null; + } + + + public function getFormatDb($table) : string { + return ($value = $this->getValueOrArray()) + ? ($this->_getOperator() . ' ' . $value->getFormatDb($table)) + : ''; + } + + + public function compare(array $a, array $b) : int { + if (!($value = $this->getValueOrArray())) + return 0; + + return ($value->getCompareValue($this->_getContents($a)) + - $value->getCompareValue($this->_getContents($b))); + } + + + public function containAttibuteInVolatile(array $model) : bool { + if (!($value = $this->getValueOrArray())) + return false; + + return $value->containVolatile($this->_getContents($model)); + } + + + protected function _getOperator() : string { + return $this->_operator . '(' . $this->_key . ')'; + } + + + protected function _clauseFormatDb() : string { + return $this->_getOperator(); + } + + + protected function _getContents(array $model) : string { + $contents = ''; + foreach (explode(',', $this->_key) as $key) + if (array_key_exists($key, $model)) + $contents = implode(' ', array_filter([$contents, $model[$key]])); + + return $contents; + } +} - if (static::CLAUSE_GREATER === $this->_operator) - return $model[$this->_key] > $this->_getValueOrArray(); - if (static::CLAUSE_GREATER_EQUAL === $this->_operator) - return $model[$this->_key] >= $this->_getValueOrArray(); - if (static::CLAUSE_LESSER === $this->_operator) - return $model[$this->_key] < $this->_getValueOrArray(); - if (static::CLAUSE_LESSER_EQUAL === $this->_operator) - return $model[$this->_key] <= $this->_getValueOrArray(); +class Storm_Model_PersistenceStrategy_ClauseLimit + extends Storm_Model_PersistenceStrategy_Clause { + + public function assemble($select) : self { + $select->limit($this->getValueOrArray()); + return $this; + } + + + public function getFormatDb($table) : string { + return ''; + } + + + public function containAttibuteInVolatile(array $model) : bool { + return true; + } +} + + + + +class Storm_Model_PersistenceStrategy_ClauseLimitPage + extends Storm_Model_PersistenceStrategy_Clause { + + public function getValueOrArray() { + return $this->_value_or_array ?? []; + } + + + public function assemble($select) : self { + $range = $this->getValueOrArray(); + if (2 <= count($range)) + $select->limitPage($range[0], $range[1]); + + 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 { + + const ORDER_DESC = ' desc'; + protected ?string $_order_mode = null; + + public function getValueOrArray() { + return $this->_value_or_array ?? null; + } + + + public function assemble($select) : self { + $select->order((($clause = $this->getValueOrArray()) + ? $clause->getFormatDb($select->getTable()) + : $this->_key) + . $this->_order_mode); + return $this; + } + + + public function getFormatDb($table) : string { + return ''; + } + + + public function containAttibuteInVolatile(array $model) : bool { + return true; + } + + + public function setOrder(bool $mode) : self { + $this->_order_mode = $mode ? static::ORDER_DESC : ''; + return $this; + } + + + public function compare(Storm_Query_Order $query_order, array $a, array $b) : int { + $compare = $this->_compareValues($a, $b) + * $this->_descendant() + * $query_order->getPosition(); + + return ($next_order = $query_order->getNextQuery()) + ? ($compare + $next_order->getClause()->compare($next_order, $a, $b)) + : $compare; + } + + + protected function _compareValues(array $a, array $b) : int { + if ($match_clause = $this->getValueOrArray()) + return $match_clause->compare($a, $b); + + $first = $this->_existKeyInModel($a) ? $a[$this->_key] : ''; + $second = $this->_existKeyInModel($b) ? $b[$this->_key] : ''; + + return ($first <=> $second); + } + - return ($this->_negated !== ($model[$this->_key] == $this->_getValueOrArray())); + protected function _descendant() : int { + return static::ORDER_DESC === $this->_order_mode ? -1 : 1; } } diff --git a/src/Storm/Model/PersistenceStrategy/Db.php b/src/Storm/Model/PersistenceStrategy/Db.php index cc5b23a87b8ec769330a2b810df832c367be01b0..c5157b57bca4c9ec2d1776f004f03e223d9a730b 100644 --- a/src/Storm/Model/PersistenceStrategy/Db.php +++ b/src/Storm/Model/PersistenceStrategy/Db.php @@ -96,7 +96,8 @@ class Storm_Model_PersistenceStrategy_Db } - protected function _generateWhereClauseForKeyAndValue(string $key, $value) : string { + protected function _generateWhereClauseForKeyAndValue(string $key, + $value) : string { $clause = ($value instanceof Storm_Model_PersistenceStrategy_Clause) ? $value : Storm_Model_PersistenceStrategy_Clause::newWith($key, $value); @@ -170,6 +171,9 @@ class Storm_Model_PersistenceStrategy_Db * @see findAllBy */ public function _generateSelectFor($args) { + if ($args instanceof Storm_Query) + return $args->assembleFor($this->getTable()->select()); + if (array_key_exists('role', $args) && array_key_exists('model', $args)) { $model = $args['model']; $role = $args['role']; @@ -185,26 +189,34 @@ class Storm_Model_PersistenceStrategy_Db else $select = $this->getTable()->select(); - foreach ($args as $field => $value) { - if (in_array($field, ['order', 'limit', 'where'], true)) { - $select->$field($value); - continue; - } + foreach ($args as $field => $value) + $this->_addInSelect($select, $field, $value); - if (in_array($field, ['limitPage', 'scope'], true)) { - $this->$field($select, $value); - continue; - } + return $select; + } - if ('group_by' === $field) { - $select->group($value); - continue; - } - $select->where($this->_generateWhereClauseForKeyAndValue($field, $value)); + protected function _addInSelect($select, string $field, $value_or_clause) : self { + if (in_array($field, ['order', 'limit', 'where'], true)) { + $select->$field($value_or_clause); + return $this; } - return $select; + if (in_array($field, ['limitPage', 'scope'], true)) { + $this->$field($select, $value_or_clause); + return $this; + } + + if ('group_by' === $field) { + $select->group($value_or_clause); + return $this; + } + + if (!($value_or_clause instanceof Storm_Model_PersistenceStrategy_Clause)) + $value_or_clause = Storm_Model_PersistenceStrategy_Clause::newWith($field, $value_or_clause); + + $select->where($this->_generateWhereClauseForKeyAndValue($field, $value_or_clause)); + return $this; } diff --git a/src/Storm/Model/PersistenceStrategy/Match.php b/src/Storm/Model/PersistenceStrategy/Match.php new file mode 100644 index 0000000000000000000000000000000000000000..e601ea22083d6a532c07f6f12cd4f234819e7d64 --- /dev/null +++ b/src/Storm/Model/PersistenceStrategy/Match.php @@ -0,0 +1,82 @@ +<?php +/* +STORM is under the MIT License (MIT) + +Copyright (c) 2010-2022 Agence Française Informatique http://www.afi-sa.fr + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +class Storm_Model_PersistenceStrategy_Match { + + protected array $_terms; + protected bool $_boolean_mode; + protected bool $_strict; + + public function __construct(array $terms, bool $strict, bool $boolean_mode) { + $this->_terms = $terms; + $this->_strict = $strict; + $this->_boolean_mode = $boolean_mode; + } + + + public function getFormatDb($table) : string { + $content = ''; + foreach ($this->_terms as $key => $term) + $content = implode(' ', array_filter([$content, + $term->getFormatDb($this->_strict)])); + + return sprintf('AGAINST(%s%s)', + $table->getAdapter()->quoteInto('?', $content, null, null), + ($this->_boolean_mode ? ' IN BOOLEAN MODE' : '')); + } + + + public function containVolatile(string $contents) : bool { + if (!$contents) + return false; + + foreach ($this->_terms as $term) { + if ($this->_strict && !$term->containVolatile($contents)) + return false; + if (!$this->_strict && $term->containVolatile($contents)) + return true; + } + + return $this->_strict; + } + + + public function getCompareValue(string $contents) : int { + $compare = 0; + + foreach ($this->_terms as $term) { + $new_compare = $term->getCompareValue($contents); + if ($this->_strict && 0 === $new_compare) + return 0; + + $compare += $term->getCompareValue($contents); + } + + return $this->_boolean_mode + ? min($compare, 1) + : ceil(($compare / count(explode(' ', $contents))) * 10); + } +} diff --git a/src/Storm/Model/PersistenceStrategy/MatchTerms.php b/src/Storm/Model/PersistenceStrategy/MatchTerms.php new file mode 100644 index 0000000000000000000000000000000000000000..bb5eb3032244eb3a00289989c3dc6af5ec44b48f --- /dev/null +++ b/src/Storm/Model/PersistenceStrategy/MatchTerms.php @@ -0,0 +1,80 @@ +<?php +/* +STORM is under the MIT License (MIT) + +Copyright (c) 2010-2022 Agence Française Informatique http://www.afi-sa.fr + +Permission is hereby granted, free of charge, to any person obtaining a copy +gof this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + + +class Storm_Model_PersistenceStrategy_MatchTerms { + + protected array $_values; + protected bool $_exact; + + public function __construct($array_or_value, ?bool $exact = false) { + if (!is_array($array_or_value)) + $array_or_value = explode(' ', $array_or_value); + + $this->_values = $array_or_value; + $this->_exact = $exact ?? false; + } + + + public function getFormatDb(bool $strict) : string { + $values = implode(' ', $this->_values); + if ($this->_exact) + return $strict ? '+"' . $values . '"' : '"' . $values . '"'; + + return $strict ? '+(' . $values . ')' : $values; + } + + + public function containVolatile(string $contents) : bool { + if ($this->_exact) + return $this->_containValue($contents, implode(' ', $this->_values)); + + foreach ($this->_values as $value) + if ($this->_containValue($contents, $value)) + return true; + + return false; + } + + + public function getCompareValue(string $contents) : int { + if ($this->_exact) + return preg_match_all('/\b' . implode(' ', $this->_values) . '\b/', $contents); + + $compare = 0; + foreach ($this->_values as $value) + $compare += ($new_compare = preg_match_all('/\b' . $value . '\b/', $contents)) + ? $new_compare + : 0; + + return $compare; + } + + + protected function _containValue(string $contents, string $value) : bool { + return preg_match('/\b' . $value . '\b/', $contents); + } +} diff --git a/src/Storm/Model/PersistenceStrategy/Volatile.php b/src/Storm/Model/PersistenceStrategy/Volatile.php index 30344a661493dd7353604c80b4449a554f89983f..7abb84075309d1a1d085dacfb1250aa6bd9f05fc 100644 --- a/src/Storm/Model/PersistenceStrategy/Volatile.php +++ b/src/Storm/Model/PersistenceStrategy/Volatile.php @@ -27,8 +27,7 @@ THE SOFTWARE. class Storm_Model_PersistenceStrategy_Volatile extends Storm_Model_PersistenceStrategy_Abstract { protected $_instances = [], - $desc_order=false, - $special_select_fields = ['order', 'limit', 'limitpage', 'where', 'group_by'], + $desc_order = false, $_table; @@ -44,26 +43,23 @@ class Storm_Model_PersistenceStrategy_Volatile extends Storm_Model_PersistenceS public function findAll($select = null) { - if (null === $filtered_select = $this->extractRoleAndModel(is_array($select) - ? array_change_key_case($select) - : [])) + if (null === $filtered = (Storm_Model_PersistenceStrategy_Volatile_Filtered::newFor($select) + ->extractRoleAndModel($this->_loader))) return []; - $group_by = $filtered_select['group_by'] ?? ''; - $order = $filtered_select['order'] ?? ''; - $limit = $filtered_select['limit'] ?? ''; + $group_by = $filtered->getGroupBy(); + $order = $filtered->getOrder(); + $limit = $filtered->getLimit(); + $limit_page = $filtered->getLimitPage(); $page_size=0; - if (isset($filtered_select['limitpage'])) { - list($page, $page_size) = $filtered_select['limitpage']; + if ($limit_page) { + list($page, $page_size) = $limit_page; if ($page > 0) $page -= 1; } - foreach($this->special_select_fields as $field) - unset($filtered_select[$field]); - $values = []; - $this->_allMatchingInstancesDo($filtered_select, + $this->_allMatchingInstancesDo($filtered->getClauses(), function($model) use (&$values) { $values []= $model; }); @@ -72,7 +68,7 @@ class Storm_Model_PersistenceStrategy_Volatile extends Storm_Model_PersistenceS $values = $this->ordered($values, $order); $values = $this->limited($values, $limit); - if ($page_size>0) + if ($page_size > 0) $values = array_slice($values, $page * $page_size, $page_size); return array_map([$this->_loader, 'newFromRow'], @@ -116,28 +112,6 @@ class Storm_Model_PersistenceStrategy_Volatile extends Storm_Model_PersistenceS } - protected function extractRoleAndModel($select) { - if (array_key_exists('role', $select) && array_key_exists('model', $select)) { - $model = $select['model']; - $role = $select['role']; - unset($select['model']); - unset($select['role']); - - if ($model->isNew()) return null; - - $field = $this->_loader->getIdFieldForDependent($role); - $select[$field]=$model->getId(); - } - - if (array_key_exists('scope', $select)) { - $select = array_merge($select, array_change_key_case($select['scope'])); - unset($select['scope']); - } - return $select; - } - - - protected function getInstancesArray() : array { return array_values( @@ -162,6 +136,9 @@ class Storm_Model_PersistenceStrategy_Volatile extends Storm_Model_PersistenceS public function ordered($result, $select) { + if ($select instanceof Storm_Query) + return $select->orderVolatile($result); + if (! ($select && $result) ) return $result; @@ -216,8 +193,8 @@ class Storm_Model_PersistenceStrategy_Volatile extends Storm_Model_PersistenceS : null; if (!$clause && preg_match('/left\((.+),(.+)\)/', $key, $matches)) - $clause = Storm_Model_PersistenceStrategy_Clause::clauseLike(trim($matches[1]), - (substr($value, 0, (int)$matches[2]) . '%')); + $clause = Storm_Model_PersistenceStrategy_Clause::like(trim($matches[1]), + (substr($value, 0, (int)$matches[2]) . '%')); if (!$clause) $clause = Storm_Model_PersistenceStrategy_Clause::newWith($key, $value); diff --git a/src/Storm/Model/PersistenceStrategy/Volatile/Filtered.php b/src/Storm/Model/PersistenceStrategy/Volatile/Filtered.php new file mode 100644 index 0000000000000000000000000000000000000000..0baff2c0cd71bd064e26e17740b47bf47dcc0f3b --- /dev/null +++ b/src/Storm/Model/PersistenceStrategy/Volatile/Filtered.php @@ -0,0 +1,121 @@ +<?php +/* +STORM is under the MIT License (MIT) + +Copyright (c) 2010-2022 Agence Française Informatique http://www.afi-sa.fr + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + + +class Storm_Model_PersistenceStrategy_Volatile_Filtered { + + protected ?Storm_Query $_query; + protected ?array $_select; + + public function __construct(?Storm_Query $query, ?array $select) { + $this->_query = $query; + $this->_select = $select; + } + + + public static function newFor($select) : self { + if ($select instanceof Storm_Query) + return (new static($select, null)); + + $select = is_array($select) + ? array_change_key_case($select) + : []; + return (new static(null, $select)); + } + + + public function extractRoleAndModel(Storm_Model_Loader $loader) : self { + if ($this->_query) + return $this; + + 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']); + unset($this->_select['role']); + + if ($model->isNew()) + return null; + + $field = $loader->getIdFieldForDependent($role); + $this->_select[$field] = $model->getId(); + } + + if (array_key_exists('scope', $this->_select)) { + $this->_select = array_merge($this->_select, array_change_key_case($this->_select['scope'])); + unset($this->_select['scope']); + } + + return $this; + } + + + public function getClauses() : array { + return $this->_query + ? $this->_query->getClauses() + : $this->_select; + } + + + public function getLimit() { + if ($this->_query) + return $this->_query->getLimitValue(); + + $limit = $this->_select['limit'] ?? ''; + unset($this->_select['limit']); + return $limit; + } + + + public function getLimitPage() { + if ($this->_query) + return $this->_query->getLimitPageValue(); + + $limit_page = $this->_select['limitpage'] ?? ''; + unset($this->_select['limitpage']); + return $limit_page; + } + + + public function getOrder() { + if ($this->_query) + return $this->_query; + + $order = $this->_select['order'] ?? ''; + unset($this->_select['order']); + return $order; + } + + + public function getGroupBy() : string { + if (!$this->_select) + return ''; + + $group_by = $this->_select['group_by'] ?? ''; + unset($this->_select['group_by']); + return $group_by; + } +} diff --git a/src/Storm/Model/PersistenceStrategy/Volatile/OrderClause.php b/src/Storm/Model/PersistenceStrategy/Volatile/OrderClause.php index 7f65cbf5ec42648f2108870587e6c2140221537b..35efd2c68a1f37f899f500a9271a592e0bdb1210 100644 --- a/src/Storm/Model/PersistenceStrategy/Volatile/OrderClause.php +++ b/src/Storm/Model/PersistenceStrategy/Volatile/OrderClause.php @@ -34,12 +34,13 @@ class Storm_Model_PersistenceStrategy_Volatile_OrderClause { $clauses = array_reverse(array_filter(explode(',', strtolower($order)))); $first_clause = new static(array_shift($clauses)); - foreach($clauses as $clause) { + foreach($clauses as $clause) $first_clause = new static($clause, $first_clause); - } + return $first_clause; } + public function __construct($order, $next_clause = null) { $description = explode(' ', trim($order)); @@ -69,12 +70,12 @@ class Storm_Model_PersistenceStrategy_Volatile_OrderClause { } - public function int_compare($a , $b) { + public function int_compare($a, $b) { return ($a == $b) ? 0 : (($a < $b) ? -1 : 1); } - public function string_compare($a, $b){ + public function string_compare($a, $b) { return strcmp($a, $b); } } diff --git a/src/Storm/Query.php b/src/Storm/Query.php new file mode 100644 index 0000000000000000000000000000000000000000..c116a582e1ce2720365fbdc1591774a26d310baa --- /dev/null +++ b/src/Storm/Query.php @@ -0,0 +1,357 @@ +<?php +/* + STORM is under the MIT License (MIT) + + Copyright (c) 2010-2022 Agence Française Informatique http://www.afi-sa.fr + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + + +class Storm_Query { + + protected array $_clauses; + protected array $_orders; + protected array $_terms; + protected Storm_Model_Loader $_loader; + protected ?Storm_Model_PersistenceStrategy_Clause $_clause_limit; + protected ?Storm_Model_PersistenceStrategy_Clause $_clause_limit_page; + + public function __construct() { + $this->_clauses = []; + $this->_orders = []; + $this->_terms = []; + $this->_clause_limit = null; + $this->_clause_limit_page = null; + } + + + public function __call($method, $args) { + return $this->_callQuery($method, $args); + } + + + public static function __callStatic($method, $args) { + return (new static)->_callQuery($method, $args); + } + + + protected function _callQuery($method, $args) { + if (!method_exists($this , '_' . $method)) + throw new Exception('Call undefined method ' . $method); + + return $this->{'_' . $method}(...$args); + } + + + public function assembleFor($select) { + foreach ($this->getClauses() as $clause) + $clause->assemble($select); + + foreach ($this->getOrders() as $order) + $order->assemble($select); + + if ($this->_clause_limit) + $this->_clause_limit->assemble($select); + + if ($this->_clause_limit_page) + $this->_clause_limit_page->assemble($select); + + return $select; + } + + + public function orderVolatile(array $models) : array { + return (new Storm_Query_Order(array_reverse($this->getOrders()))) + ->compareVolatile($models); + } + + + public function getClauses() : array { + return $this->_clauses; + } + + + public function getOrders() : array { + return $this->_orders; + } + + + public function getTerms() : array { + return $this->_terms; + } + + + public function getLimitValue() { + return $this->_clause_limit ? $this->_clause_limit->getValueOrArray() : ''; + } + + + public function getLimitPageValue() : ?array { + return $this->_clause_limit_page ? $this->_clause_limit_page->getValueOrArray() : null; + } + + + protected function _limit($limit) : self { + $this->_clause_limit = Storm_Model_PersistenceStrategy_Clause::limit($limit); + return $this; + } + + + protected function _limit_page(array $range) { + $this->_clause_limit_page = Storm_Model_PersistenceStrategy_Clause::limit_page($range); + return $this; + } + + + protected function _eq(string $key, string $value) : self { + $this->_clauses [] = Storm_Model_PersistenceStrategy_Clause::equal($key, $value); + return $this; + } + + + public function _not_eq(string $key, string $value) : self { + $this->_clauses [] = Storm_Model_PersistenceStrategy_Clause::equal($key, $value)->setNegated(true); + return $this; + } + + + protected function _like(string $key, string $value) : self { + $this->_clauses [] = Storm_Model_PersistenceStrategy_Clause::like($key, $value); + return $this; + } + + + protected function _not_like(string $key, string $value) : self { + $this->_clauses [] = Storm_Model_PersistenceStrategy_Clause::like($key, $value)->setNegated(true); + return $this; + } + + + protected function _gt(string $key, string $value) : self { + $this->_clauses [] = Storm_Model_PersistenceStrategy_Clause::greater($key, $value); + return $this; + } + + + protected function _gt_eq(string $key, string $value) : self { + $this->_clauses [] = Storm_Model_PersistenceStrategy_Clause::greaterEqual($key, $value); + return $this; + } + + + protected function _lt(string $key, string $value) : self { + $this->_clauses [] = Storm_Model_PersistenceStrategy_Clause::lesser($key, $value); + return $this; + } + + + protected function _lt_eq(string $key, string $value) : self { + $this->_clauses [] = Storm_Model_PersistenceStrategy_Clause::lesserEqual($key, $value); + return $this; + } + + + protected function _is(string $key) : self { + $this->_clauses [] = Storm_Model_PersistenceStrategy_Clause::is($key); + return $this; + } + + + protected function _not_is(string $key) : self { + $this->_clauses [] = Storm_Model_PersistenceStrategy_Clause::is($key)->setNegated(true); + return $this; + } + + + protected function _in(string $key, array $array) : self { + $this->_clauses [] = Storm_Model_PersistenceStrategy_Clause::in($key, $array); + return $this; + } + + + protected function _not_in(string $key, array $array) : self { + $this->_clauses [] = Storm_Model_PersistenceStrategy_Clause::in($key, $array)->setNegated(true); + return $this; + } + + + protected function _start(string $key, string $value) : self { + $this->_clauses [] = Storm_Model_PersistenceStrategy_Clause::start($key, $value); + return $this; + } + + + protected function _end(string $key, string $value) : self { + $this->_clauses [] = Storm_Model_PersistenceStrategy_Clause::end($key, $value); + return $this; + } + + + protected function _or(Storm_Query ...$querys) : self { + $clauses = []; + foreach ($querys as $q) + $clauses = [...$clauses, ...$q->getClauses()]; + $this->_clauses [] = Storm_Model_PersistenceStrategy_Clause::or($clauses); + return $this; + } + + + protected function _and(Storm_Query ...$querys) : self { + $clauses = []; + foreach ($querys as $q) + $clauses = [...$clauses, ...$q->getClauses()]; + $this->_clauses [] = Storm_Model_PersistenceStrategy_Clause::and($clauses); + return $this; + } + + + protected function _match(string $key, + Storm_Query $query, + ?bool $strict = false, + ?bool $boolean_mode = true) : self { + $this->_clauses [] = Storm_Model_PersistenceStrategy_Clause::match($key, + $query->getTerms(), + $strict, + $boolean_mode); + return $this; + } + + + protected function _terms($value_or_array, ?bool $exact = false) : self { + $this->_terms [] = (new Storm_Model_PersistenceStrategy_MatchTerms($value_or_array, $exact)); + return $this; + } + + + protected function _order(string $key) : self { + $this->_orders [] = Storm_Model_PersistenceStrategy_Clause::order($key)->setOrder(false); + return $this; + } + + + protected function _order_desc(string $key) : self { + $this->_orders [] = Storm_Model_PersistenceStrategy_Clause::order($key)->setOrder(true); + return $this; + } + + + protected function _order_match(string $key, + Storm_Query $query, + ?bool $strict = false, + ?bool $boolean_mode = true) : self { + $match = Storm_Model_PersistenceStrategy_Clause::match($key, + $query->getTerms(), + $strict, + $boolean_mode); + $this->_orders [] = Storm_Model_PersistenceStrategy_Clause::order($key, $match)->setOrder(false); + return $this; + } + + + protected function _order_match_desc(string $key, + Storm_Query $query, + ?bool $strict = false, + ?bool $boolean_mode = true) : self { + $match = Storm_Model_PersistenceStrategy_Clause::match($key, + $query->getTerms(), + $strict, + $boolean_mode); + $this->_orders [] = Storm_Model_PersistenceStrategy_Clause::order($key, $match)->setOrder(true); + return $this; + } + + + protected function _from(Storm_Model_Loader $loader) : self { + $this->_loader = $loader; + return $this; + } + + + protected function _fetchAll() : array { + return $this->_loader + ? $this->_loader->getPersistenceStrategy()->findAllBy($this) + : []; + } + + + protected function _count() : array { + return $this->_loader + ? $this->_loader->getPersistenceStrategy()->countBy($this) + : []; + } + + + protected function _fetchFirst() : Storm_Model_Abstract { + return $this->_loader + ? $this->_loader->getPersistenceStrategy()->findFirstBy($this) + : null; + } +} + + + + +class Storm_Query_Order { + + protected ?Storm_Query_Order $_next_query; + protected ?Storm_Model_PersistenceStrategy_Clause $_clause; + protected int $_position; + + public function __construct(array $orders, ?int $position = 0) { + $this->_position = $position++; + $this->_clause = array_shift($orders); + + $this->_next_query = ($orders + ? (new Storm_Query_Order($orders, $position)) + : null); + } + + + public function getNextQuery() : ?Storm_Query_Order { + if (!($next_query = $this->_next_query)) + return null; + + return $next_query->getClause() + ? $next_query + : null; + } + + + public function getClause() : ?Storm_Model_PersistenceStrategy_Clause { + return $this->_clause; + } + + + public function getPosition() : int { + return 10 ** $this->_position; + } + + + public function compareVolatile(array $models) : array { + if (!$models) + return $models; + + if ($clause = $this->getClause()) + usort($models, fn($a, $b) => $clause->compare($this, $a, $b)); + + return $models; + } +} diff --git a/tests/Storm/Test/LoaderTest.php b/tests/Storm/Test/LoaderTest.php index e7ab7352007a52b1be079896e130fe46807df2ad..d30618935d4a0dcf4d1768d747ddd92e2ea6e647 100644 --- a/tests/Storm/Test/LoaderTest.php +++ b/tests/Storm/Test/LoaderTest.php @@ -1,26 +1,26 @@ <?php /* -STORM is under the MIT License (MIT) - -Copyright (c) 2010-2011 Agence Française Informatique http://www.afi-sa.fr - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. + STORM is under the MIT License (MIT) + + Copyright (c) 2010-2011 Agence Française Informatique http://www.afi-sa.fr + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. */ @@ -38,15 +38,15 @@ abstract class Storm_Test_LoaderTestCase extends Storm_Test_ModelTestCase { ->whenCalled('quoteInto') ->willDo(function($clause, $value) - { - return str_replace('?', - is_array($value) - ? implode(',', - array_map(function($item){ return "'" . $item . "'"; }, - $value)) - : "'" . $value . "'", - $clause); - }); + { + return str_replace('?', + is_array($value) + ? implode(',', + array_map(function($item){ return "'" . $item . "'"; }, + $value)) + : "'" . $value . "'", + $clause); + }); $this->_loader = Storm_Test_Mock_User::getLoader()->setTable($this->_table); } @@ -168,31 +168,6 @@ class Storm_Test_LoaderBasicTest extends Storm_Test_LoaderTestCase { [ ['where' => 'id=\'29\' and name=\'lp\''], ['id=\'29\' and name=\'lp\''] ], [ ['left(name, 5)' => 'ganda'], ['left(name, 5)=\'ganda\''] ], [ ['id' => '29'], ['id=\'29\''] ], - [ [ Storm_Model_PersistenceStrategy_Clause::clauseGreater('id', '30') ], - ['id>\'30\''] ], - [ [ Storm_Model_PersistenceStrategy_Clause::clauseGreaterEqual('id', '30') ], - ['id>=\'30\''] ], - [ [ Storm_Model_PersistenceStrategy_Clause::clauseLesser('id', '30') ], - ['id<\'30\''] ], - [ [ Storm_Model_PersistenceStrategy_Clause::clauseLesserEqual('id', '30') ], - ['id<=\'30\''] ], - [ [ Storm_Model_PersistenceStrategy_Clause::clauseIs('name') ], ['name is null'] ], - [ [ Storm_Model_PersistenceStrategy_Clause::clauseIn('name', ['Harlock', 'Nausicaa']) ], - ['name in (\'Harlock\',\'Nausicaa\')'] ], - [ [ Storm_Model_PersistenceStrategy_Clause::clauseNotEqual('name', 'Harlock') ], - ['name!=\'Harlock\''] ], - [ [ Storm_Model_PersistenceStrategy_Clause::clauseNotIn('name', ['Harlock', 'Nausicaa']) ], - ['name not in (\'Harlock\',\'Nausicaa\')'] ], - [ [ Storm_Model_PersistenceStrategy_Clause::clauseLike('login', '%aus%') ], - ['login like \'%aus%\''] ], - [ [ Storm_Model_PersistenceStrategy_Clause::clauseNotLike('login', '%aus%') ], - ['login not like \'%aus%\''] ], - [ [ Storm_Model_PersistenceStrategy_Clause::clauseEqual('left(name, 5)', 'ganda') ], - ['left(name, 5)=\'ganda\''] ], - [ [ Storm_Model_PersistenceStrategy_Clause::clauseStart('login', 'aus') ], - ['login like \'aus%\''] ], - [ [ Storm_Model_PersistenceStrategy_Clause::clauseEnd('login', 'aus') ], - ['login like \'%aus\''] ], ]; } @@ -360,3 +335,397 @@ class Storm_Test_LoaderSaveWithIdTest extends Storm_Test_LoaderTestCase { $this->_table->getFirstAttributeForLastCallOn('update')); } } + + + + +class Storm_Test_LoaderQueryTest 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('where')->answers($this->_select) + ->whenCalled('getTable')->answers($this->_table); + } + + + /** @test */ + public function withClauseGreaterThanShouldReturnQuery() { + Storm_Query::gt('id', '30') + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['id>\'30\''], + $this->_select->getAttributesForLastCallOn('where')); + } + + + /** @test */ + public function withClauseGreaterThanEqualShouldReturnQuery() { + Storm_Query::gt_eq('id', '30') + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['id>=\'30\''], + $this->_select->getAttributesForLastCallOn('where')); + } + + + /** @test */ + public function withClauseLesserThanShouldReturnQuery() { + Storm_Query::lt('id', '30') + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['id<\'30\''], + $this->_select->getAttributesForLastCallOn('where')); + } + + + /** @test */ + public function withClauseLesserThanEqualShouldReturnQuery() { + Storm_Query::lt_eq('id', '30') + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['id<=\'30\''], + $this->_select->getAttributesForLastCallOn('where')); + } + + + /** @test */ + public function withClauseIsShouldReturnQuery() { + Storm_Query::is('name') + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['name is null'], + $this->_select->getAttributesForLastCallOn('where')); + } + + + /** @test */ + public function withClauseIsNotShouldReturnQuery() { + Storm_Query::not_is('name') + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['name is not null'], + $this->_select->getAttributesForLastCallOn('where')); + } + + + /** @test */ + public function withClauseInShouldReturnQuery() { + Storm_Query::in('name', ['Harlock', 'Nausicaa']) + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['name in (\'Harlock\',\'Nausicaa\')'], + $this->_select->getAttributesForLastCallOn('where')); + } + + + /** @test */ + public function withClauseNotInShouldReturnQuery() { + Storm_Query::not_in('name', ['Harlock', 'Nausicaa']) + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['name not in (\'Harlock\',\'Nausicaa\')'], + $this->_select->getAttributesForLastCallOn('where')); + } + + + /** @test */ + public function withClauseEqualShouldReturnQuery() { + Storm_Query::eq('left(name, 5)', 'ganda') + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['left(name, 5)=\'ganda\''], + $this->_select->getAttributesForLastCallOn('where')); + } + + + /** @test */ + public function withClauseNotEqualShouldReturnQuery() { + Storm_Query::not_eq('name', 'Harlock') + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['name!=\'Harlock\''], + $this->_select->getAttributesForLastCallOn('where')); + } + + + /** @test */ + public function withClauseLikeShouldReturnQuery() { + Storm_Query::like('login', '%aus%') + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['login like \'%aus%\''], + $this->_select->getAttributesForLastCallOn('where')); + } + + + /** @test */ + public function withClauseNotLikeShouldReturnQuery() { + Storm_Query::not_like('login', '%aus%') + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['login not like \'%aus%\''], + $this->_select->getAttributesForLastCallOn('where')); + } + + + /** @test */ + public function withClauseStartShouldReturnQuery() { + Storm_Query::start('login', 'aus') + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['login like \'aus%\''], + $this->_select->getAttributesForLastCallOn('where')); + } + + + /** @test */ + public function withClauseEndShouldReturnQuery() { + Storm_Query::end('login', 'aus') + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['login like \'%aus\''], + $this->_select->getAttributesForLastCallOn('where')); + } + + + /** @test */ + public function withClauseOrShouldReturnQuery() { + Storm_Query::or(Storm_Query::eq('login', 'aus'), + Storm_Query::in('role', ['admin', 'invite'])) + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['(login=\'aus\' or role in (\'admin\',\'invite\'))'], + $this->_select->getAttributesForLastCallOn('where')); + } + + + /** @test */ + public function withClauseAndShouldReturnQuery() { + Storm_Query::and(Storm_Query::eq('login', 'aus')->in('role', ['admin', 'invite'])) + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['(login=\'aus\' and role in (\'admin\',\'invite\'))'], + $this->_select->getAttributesForLastCallOn('where')); + } + + + /** @test */ + public function withClauseMatchShouldReturnQueryInBooleanMode() { + Storm_Query::match('login, role', Storm_Query::terms(['ADMIN', 'INVITE'])) + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['MATCH(login, role) AGAINST(\'ADMIN INVITE\' IN BOOLEAN MODE)'], + $this->_select->getAttributesForLastCallOn('where')); + } + + + /** @test */ + public function withClauseMatchMultipleTermsShouldReturnQueryInBooleanMode() { + Storm_Query::match('login,role', Storm_Query::terms('ADMIN INVITE')->terms('HUGO ADRIEN')) + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['MATCH(login,role) AGAINST(\'ADMIN INVITE HUGO ADRIEN\' IN BOOLEAN MODE)'], + $this->_select->getAttributesForLastCallOn('where')); + } + + + /** @test */ + public function withClauseMatchStrictModeShouldRturnQueryInBooleanMode() { + Storm_Query::match('login,role', Storm_Query::terms('ADMIN INVITE')->terms('HUGO ADRIEN'), + true) + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['MATCH(login,role) AGAINST(\'+(ADMIN INVITE) +(HUGO ADRIEN)\' IN BOOLEAN MODE)'], + $this->_select->getAttributesForLastCallOn('where')); + } + + + /** @test */ + public function withClauseMatchStrictAndOneExactExpressionWithNoBooleanModeShouldReturnQuery() { + Storm_Query::match('login,role', Storm_Query::terms('ADMIN INVITE', true)->terms('HUGO ADRIEN'), + true, + false) + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['MATCH(login,role) AGAINST(\'+"ADMIN INVITE" +(HUGO ADRIEN)\')'], + $this->_select->getAttributesForLastCallOn('where')); + } +} + + + + +class Storm_Test_LoaderQueryLimitTest 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('limit')->answers($this->_select); + } + + + /** @test */ + public function withClauseLimit30ShouldReturnQuery() { + Storm_Query::limit(30) + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals([30], + $this->_select->getAttributesForLastCallOn('limit')); + } + + + /** @test */ + public function withClauseLimit30Offset10ShouldReturnQuery() { + Storm_Query::limit('10, 30') + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['10, 30'], + $this->_select->getAttributesForLastCallOn('limit')); + } +} + + + + +class Storm_Test_LoaderQueryLimitPageTest 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('limitPage')->answers($this->_select); + } + + + /** @test */ + public function withClauseLimitPageShouldReturnQuery() { + Storm_Query::limit_page([1, 100]) + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals([1, 100], + $this->_select->getAttributesForLastCallOn('limitPage')); + } +} + + + + +class Storm_Test_LoaderQueryOrderTest 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('order')->answers($this->_select) + ->whenCalled('getTable')->answers($this->_table); + } + + + /** @test */ + public function withClauseOrderIdShouldReturnOrderId() { + Storm_Query::order('id') + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['id'], + $this->_select->getAttributesForLastCallOn('order')); + } + + + /** @test */ + public function withClauseOrderIdDescShouldReturnOrderIdDesc() { + Storm_Query::order_desc('id') + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['id desc'], + $this->_select->getAttributesForLastCallOn('order')); + } + + + /** @test */ + public function withMultipleClauseOrderShouldReturnAllOrders() { + Storm_Query::order_desc('id') + ->order('name') + ->order_desc('role') + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['id desc', 'name', 'role desc'], + [$this->_select->getFirstAttributeForMethodCallAt('order', 0), + $this->_select->getFirstAttributeForMethodCallAt('order', 1), + $this->_select->getFirstAttributeForMethodCallAt('order', 2)]); + } + + + /** @test */ + public function withMatchClauseOrderShouldReturnMatchOrder() { + Storm_Query::order_match('login, role', Storm_Query::terms(['ADMIN', 'INVITE']), false, false) + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['MATCH(login, role) AGAINST(\'ADMIN INVITE\')'], + $this->_select->getAttributesForLastCallOn('order')); + } + + + /** @test */ + public function withMatchClauseOrderDescShouldReturnMatchOrder() { + Storm_Query::order_match_desc('login, role', Storm_Query::terms(['ADMIN', 'INVITE'])) + ->from($this->_loader) + ->fetchAll(); + + $this->assertEquals(['MATCH(login, role) AGAINST(\'ADMIN INVITE\' IN BOOLEAN MODE) desc'], + $this->_select->getAttributesForLastCallOn('order')); + } +} diff --git a/tests/Storm/Test/LoaderVolatileTest.php b/tests/Storm/Test/LoaderVolatileTest.php index 3e91fdf9f94fdd2e3b9bbaab7b14f7854ecb00be..09c2aa8f46d9cb52ed25a8338328303e6793f048 100644 --- a/tests/Storm/Test/LoaderVolatileTest.php +++ b/tests/Storm/Test/LoaderVolatileTest.php @@ -148,7 +148,6 @@ class Storm_Test_LoaderVolatileTest extends Storm_Test_ModelTestCase { 'level' => 'invite', 'brain_id' => null, 'id_mouth' => null]); - } @@ -608,6 +607,24 @@ class Storm_Test_LoaderVolatileTest extends Storm_Test_ModelTestCase { } + /** @test */ + public function withClauseLimitOneShouldReturnOnlyUserOneAlbert() { + $this->assertEquals([$this->albert], + Storm_Query::limit(1) + ->from(Storm_Test_VolatileUser::getLoader()) + ->fetchAll()); + } + + + /** @test */ + public function withClauseLimitOneTwoShouldReturnTwoUserHubertAndZoe() { + $this->assertEquals([$this->hubert, $this->zoe], + Storm_Query::limit('1, 2') + ->from(Storm_Test_VolatileUser::getLoader()) + ->fetchAll()); + } + + /** @test */ public function deleteByLevelInviteShouldDeleteHubertAndMax() { Storm_Test_VolatileUser::deleteBy(['level' => 'invite']); @@ -655,6 +672,33 @@ class Storm_Test_LoaderVolatileTest extends Storm_Test_ModelTestCase { } + /** @test */ + public function withClauseLimitPageShouldPaginate() { + $this->assertEquals([$this->albert, $this->hubert], + Storm_Query::from(Storm_Test_VolatileUser::getLoader()) + ->limit_page([0, 2]) + ->fetchAll()); + $this->assertEquals([$this->albert, $this->hubert], + Storm_Query::from(Storm_Test_VolatileUser::getLoader()) + ->limit_page([1, 2]) + ->fetchAll()); + $this->assertEquals([$this->zoe], + Storm_Query::from(Storm_Test_VolatileUser::getLoader()) + ->limit_page([2, 2]) + ->fetchAll()); + } + + + /** @test */ + public function withClauseLimitPageAndAttributeShouldWork() { + $this->assertEquals([$this->hubert, $this->zoe], + Storm_Query::from(Storm_Test_VolatileUser::getLoader()) + ->eq('foo', 'snafu') + ->limit_page([1, 2]) + ->fetchAll()); + } + + /** @test */ public function memoryReportShouldPrintThreeUsers() { $this->assertContains("Storm_Test_VolatileUser: 3", @@ -782,7 +826,9 @@ class Storm_Test_LoaderVolatileTest extends Storm_Test_ModelTestCase { /** @test */ public function selectAllCatsWhereIdMoreThanThreeShouldAnswersFifiAndLoulou() { $this->assertEquals(['fifi', 'loulou'], - (new Storm_Model_Collection(Storm_Test_VolatileCat::findAllBy([Storm_Test_VolatileCat::clauseGreater('id', 3)]))) + (new Storm_Model_Collection(Storm_Query::gt('id', 3) + ->from(Storm_Test_VolatileCat::getLoader()) + ->fetchAll())) ->collect('name') ->getArrayCopy()); } @@ -791,7 +837,9 @@ class Storm_Test_LoaderVolatileTest extends Storm_Test_ModelTestCase { /** @test */ public function selectAllCatsWhereIdMoreOrEqualThanForShouldAnswersFifiAndLoulou() { $this->assertEquals(['fifi', 'loulou'], - (new Storm_Model_Collection(Storm_Test_VolatileCat::findAllBy([Storm_Test_VolatileCat::clauseGreaterEqual('id', 4)]))) + (new Storm_Model_Collection(Storm_Query::gt_eq('id', 4) + ->from(Storm_Test_VolatileCat::getLoader()) + ->fetchAll())) ->collect('name') ->getArrayCopy()); } @@ -800,7 +848,9 @@ class Storm_Test_LoaderVolatileTest extends Storm_Test_ModelTestCase { /** @test */ public function selectAllCatsWhereIdLesserThanFiveShouldAnswersRiriAndFifi() { $this->assertEquals(['riri', 'fifi'], - (new Storm_Model_Collection(Storm_Test_VolatileCat::findAllBy([Storm_Test_VolatileCat::clauseLesser('id', 5)]))) + (new Storm_Model_Collection(Storm_Query::lt('id', 5) + ->from(Storm_Test_VolatileCat::getLoader()) + ->fetchAll())) ->collect('name') ->getArrayCopy()); } @@ -809,7 +859,9 @@ class Storm_Test_LoaderVolatileTest extends Storm_Test_ModelTestCase { /** @test */ public function selectAllCatsWhereIdLesserOrEqualThanForShouldAnswersRiriAndFifi() { $this->assertEquals(['riri', 'fifi'], - (new Storm_Model_Collection(Storm_Test_VolatileCat::findAllBy([Storm_Test_VolatileCat::clauseLesserEqual('id', 4)]))) + (new Storm_Model_Collection(Storm_Query::lt_eq('id', 4) + ->from(Storm_Test_VolatileCat::getLoader()) + ->fetchAll())) ->collect('name') ->getArrayCopy()); } @@ -818,7 +870,9 @@ class Storm_Test_LoaderVolatileTest extends Storm_Test_ModelTestCase { /** @test */ public function selectUserLoginNotHubertShouldReturnAlbertAndZoe() { $this->assertEquals(['albert', 'zoe'], - (new Storm_Model_Collection(Storm_Test_VolatileUser::findAllBy([Storm_Test_VolatileUser::clauseNotEqual('login', 'hubert')]))) + (new Storm_Model_Collection(Storm_Query::not_eq('login', 'hubert') + ->from(Storm_Test_VolatileUser::getLoader()) + ->fetchAll())) ->collect('login') ->getArrayCopy()); } @@ -827,7 +881,10 @@ class Storm_Test_LoaderVolatileTest extends Storm_Test_ModelTestCase { /** @test */ public function selectUserLoginInAlbertZoeAndFooNotLikeSnafuShouldReturnAlbert() { $this->assertEquals(['albert'], - (new Storm_Model_Collection(Storm_Test_VolatileUser::findAllBy([Storm_Test_VolatileUser::clauseIn('login', ['albert', 'zoe']), Storm_Test_VolatileUser::clauseNotLike('foo', '%naf%')]))) + (new Storm_Model_Collection(Storm_Query::in('login', ['albert', 'zoe']) + ->not_like('foo', '%naf%') + ->from(Storm_Test_VolatileUser::getLoader()) + ->fetchAll())) ->collect('login') ->getArrayCopy()); } @@ -836,7 +893,10 @@ class Storm_Test_LoaderVolatileTest extends Storm_Test_ModelTestCase { /** @test */ public function selectUserLoginInAlbertZoeAndFooStartSnaShouldReturnZoe() { $this->assertEquals(['zoe'], - (new Storm_Model_Collection(Storm_Test_VolatileUser::findAllBy([Storm_Test_VolatileUser::clauseIn('login', ['albert', 'zoe']), Storm_Test_VolatileUser::clauseStart('foo', 'sna')]))) + (new Storm_Model_Collection(Storm_Query::in('login', ['albert', 'zoe']) + ->start('foo', 'sna') + ->from(Storm_Test_VolatileUser::getLoader()) + ->fetchAll())) ->collect('login') ->getArrayCopy()); } @@ -845,7 +905,10 @@ class Storm_Test_LoaderVolatileTest extends Storm_Test_ModelTestCase { /** @test */ public function selectUserLoginInAlbertZoeAndFooEndAfuShouldReturnZoe() { $this->assertEquals(['zoe'], - (new Storm_Model_Collection(Storm_Test_VolatileUser::findAllBy([Storm_Test_VolatileUser::clauseIn('login', ['albert', 'zoe']), Storm_Test_VolatileUser::clauseEnd('foo', 'afu')]))) + (new Storm_Model_Collection(Storm_Query::in('login', ['albert', 'zoe']) + ->end('foo', 'afu') + ->from(Storm_Test_VolatileUser::getLoader()) + ->fetchAll())) ->collect('login') ->getArrayCopy()); } @@ -859,3 +922,237 @@ class Storm_Test_LoaderVolatileTest extends Storm_Test_ModelTestCase { ->getArrayCopy()); } } + + + + +class Storm_Test_LoaderVolatileClauseWhereTest extends Storm_Test_ModelTestCase { + + public function setUp() { + parent::setUp(); + + $this->fixture(Storm_Test_VolatileUser::class, + ['id' => 100, + 'login' => 'user_admin', + 'level' => 'invite admin redacteur', + 'foo' => 'premier deuxieme troisieme']); + + $this->fixture(Storm_Test_VolatileUser::class, + ['id' => 101, + 'login' => 'user_administrateur', + 'level' => 'administrateur', + 'foo' => 'deuxieme redacteur']); + + $this->fixture(Storm_Test_VolatileUser::class, + ['id' => 102, + 'login' => 'user_invite', + 'level' => 'invite', + 'foo' => 'premier deuxieme']); + } + + + /** @test */ + public function withMatchLevelAgainstAdminShouldAnswersOnlyUser_Admin() { + $this->assertEquals(['user_admin'], + (new Storm_Model_Collection(Storm_Query::match('level', Storm_Query::terms(['admin'])) + ->from(Storm_Test_VolatileUser::getLoader()) + ->fetchAll())) + ->collect('login') + ->getArrayCopy()); + } + + + /** @test */ + public function withMatchLevelAgainstAdminDeuxiemeShouldAnswersUser_AdminAndUser_AdministrateurAndUser_Invite() { + $this->assertEquals(['user_admin', 'user_administrateur', 'user_invite'], + (new Storm_Model_Collection(Storm_Query::match('level,foo', + Storm_Query::terms(['admin', 'deuxieme'])) + ->from(Storm_Test_VolatileUser::getLoader()) + ->fetchAll())) + ->collect('login') + ->getArrayCopy()); + } + + + /** @test */ + public function withMatchLevelAgainstStrictAdminRedacteur_DeuxiemeShouldAnswersUser_AdminAndUser_Administrateur() { + $this->assertEquals(['user_admin', 'user_administrateur'], + (new Storm_Model_Collection(Storm_Query::match('level,foo', + Storm_Query::terms(['admin', 'redacteur']) + ->terms(['deuxieme']), + true) + ->from(Storm_Test_VolatileUser::getLoader()) + ->fetchAll())) + ->collect('login') + ->getArrayCopy()); + } + + + /** @test */ + public function withMatchLevelAgainstExactDeuxiemeTroisiemeShouldAnswersOnlyUser_Admin() { + $this->assertEquals(['user_administrateur'], + (new Storm_Model_Collection(Storm_Query::match('level,foo', + Storm_Query::terms('deuxieme redacteur', + true)) + ->from(Storm_Test_VolatileUser::getLoader()) + ->fetchAll())) + ->collect('login') + ->getArrayCopy()); + } + + + /** @test */ + public function withOrClause_LevelInviteOrFooLikePremierShouldAnswersUser_AdminAndUser_Invite() { + $this->assertEquals(['user_admin', 'user_invite'], + (new Storm_Model_Collection(Storm_Query::from(Storm_Test_VolatileUser::getLoader()) + ->or(Storm_Query::eq('level', 'invite') + ->like('foo', '%premier%')) + ->fetchAll())) + ->collect('login') + ->getArrayCopy()); + } + + + /** @test */ + public function withAndClause_LevelInviteOrFooLikePremierShouldAnswersOnlyUser_Invite() { + $this->assertEquals(['user_invite'], + (new Storm_Model_Collection(Storm_Query::from(Storm_Test_VolatileUser::getLoader()) + ->and(Storm_Query::eq('level', 'invite') + ->like('foo', '%premier%')) + ->fetchAll())) + ->collect('login') + ->getArrayCopy()); + } +} + + + + +class Storm_Test_LoaderVolatileClauseOrderTest extends Storm_Test_ModelTestCase { + + public function setUp() { + parent::setUp(); + + $this->fixture(Storm_Test_VolatileUser::class, + ['id' => 100, + 'login' => 'user_admin', + 'level' => 'invite admin redacteur', + 'foo' => 'first second third', + 'order1' => 'abcd', + 'order2' => 2, + 'order3' => 'defg']); + + $this->fixture(Storm_Test_VolatileUser::class, + ['id' => 101, + 'login' => 'user_administrateur', + 'level' => 'administrateur', + 'foo' => 'third second forth', + 'order1' => 'abcdef', + 'order2' => 3, + 'order3' => 'defg']); + + $this->fixture(Storm_Test_VolatileUser::class, + ['id' => 102, + 'login' => 'user_invite', + 'level' => 'invite', + 'foo' => 'forth fifth seven', + 'order1' => 'cdef', + 'order2' => 1, + 'order3' => 'klmn']); + } + + + /** @test */ + public function with_Order1Desc_ShouldAnswersInOrdered() { + $this->assertEquals(['user_invite', 'user_administrateur', 'user_admin'], + (new Storm_Model_Collection(Storm_Query::from(Storm_Test_VolatileUser::getLoader()) + ->order_desc('order1') + ->fetchAll())) + ->collect('login') + ->getArrayCopy()); + } + + + /** @test */ + public function with_Order3Asc_Order2Desc_ShouldAnswersOrderedUser() { + $this->assertEquals(['user_administrateur', 'user_admin', 'user_invite'], + (new Storm_Model_Collection(Storm_Query::from(Storm_Test_VolatileUser::getLoader()) + ->order('order3') + ->order_desc('order2') + ->fetchAll())) + ->collect('login') + ->getArrayCopy()); + } + + + /** @test */ + public function with_Order3Desc_Order2Desc_ShouldAnswersOrderedUser() { + $this->assertEquals(['user_invite', 'user_administrateur', 'user_admin'], + (new Storm_Model_Collection(Storm_Query::from(Storm_Test_VolatileUser::getLoader()) + ->order_desc('order3') + ->order_desc('order2') + ->fetchAll())) + ->collect('login') + ->getArrayCopy()); + } + + + /** @test */ + public function withMatch_Level_Administrateur_ShouldAnswersOrderedUser() { + $query = Storm_Query::from(Storm_Test_VolatileUser::getLoader()) + ->order_match('level', Storm_Query::terms('administrateur'), + false, false); + $this->assertEquals(['user_admin', 'user_invite', 'user_administrateur'], + (new Storm_Model_Collection($query->fetchAll())) + ->collect('login') + ->getArrayCopy()); + } + + + /** @test */ + public function withMatch_Level_Administrateur_Redacteur_Desc_ShouldAnswersOrderedUser() { + $query = Storm_Query::from(Storm_Test_VolatileUser::getLoader()) + ->order_match_desc('level', Storm_Query::terms('administrateur redacteur'), + false, false); + $this->assertEquals(['user_administrateur', 'user_admin', 'user_invite'], + (new Storm_Model_Collection($query->fetchAll())) + ->collect('login') + ->getArrayCopy()); + } + + + /** @test */ + public function withMatchInBooleanMode_Level_Administrateur_Redacteur_Desc_ShouldAnswersOrderedUser() { + $query = Storm_Query::from(Storm_Test_VolatileUser::getLoader()) + ->order_match_desc('level', Storm_Query::terms('administrateur redacteur')); + $this->assertEquals(['user_admin', 'user_administrateur', 'user_invite'], + (new Storm_Model_Collection($query->fetchAll())) + ->collect('login') + ->getArrayCopy()); + } + + + /** @test */ + public function withMatch_FooDescExacteInBooleanMode_Forth_Fifth_Level_Administrateur_Redacteur_ShouldAnswersOrderedUser() { + $query = Storm_Query::from(Storm_Test_VolatileUser::getLoader()) + ->order_match_desc('foo', Storm_Query::terms('forth fifth', true)) + ->order_match('level', Storm_Query::terms('administrateur redacteur'), + false, false); + $this->assertEquals(['user_invite', 'user_admin', 'user_administrateur'], + (new Storm_Model_Collection($query->fetchAll())) + ->collect('login') + ->getArrayCopy()); + } + + + /** @test */ + public function withMatch_FooLevelStrict_InviteRedacteur_SecondThird_ShouldAnswersOrderedUser() { + $query = Storm_Query::from(Storm_Test_VolatileUser::getLoader()) + ->order_match('foo,level', Storm_Query::terms('invite redacteur') + ->terms('second third'), true); + $this->assertEquals(['user_administrateur', 'user_invite', 'user_admin'], + (new Storm_Model_Collection($query->fetchAll())) + ->collect('login') + ->getArrayCopy()); + } +}