diff --git a/extensions/elasticsearch/CHANGELOG.md b/extensions/elasticsearch/CHANGELOG.md index c74820e..eb27657 100644 --- a/extensions/elasticsearch/CHANGELOG.md +++ b/extensions/elasticsearch/CHANGELOG.md @@ -9,6 +9,7 @@ Yii Framework 2 elasticsearch extension Change Log - Enh #1313: made index and type available in `ActiveRecord::instantiate()` to allow creating records based on elasticsearch type when doing cross index/type search (cebe) - Enh #1382: Added a debug toolbar panel for elasticsearch (cebe) - Enh #1765: Added support for primary key path mapping, pk can now be part of the attributes when mapping is defined (cebe) +- Enh #2002: Added filterWhere() method to yii\elasticsearch\Query to allow easy addition of search filter conditions by ignoring empty search fields (samdark, cebe) - Enh #2892: ActiveRecord dirty attributes are now reset after call to `afterSave()` so information about changed attributes is available in `afterSave`-event (cebe) - Chg #1765: Changed handling of ActiveRecord primary keys, removed getId(), use getPrimaryKey() instead (cebe) - Chg #2281: Renamed `ActiveRecord::create()` to `populateRecord()` and changed signature. This method will not call instantiate() anymore (cebe) diff --git a/extensions/elasticsearch/Query.php b/extensions/elasticsearch/Query.php index e44adf3..c9ab5f2 100644 --- a/extensions/elasticsearch/Query.php +++ b/extensions/elasticsearch/Query.php @@ -96,6 +96,7 @@ class Query extends Component implements QueryInterface public $facets = []; + public function init() { parent::init(); @@ -271,7 +272,6 @@ class Query extends Component implements QueryInterface foreach ($result['hits']['hits'] as $row) { $column[] = isset($row['fields'][$field]) ? $row['fields'][$field] : null; } - return $column; } @@ -316,7 +316,6 @@ class Query extends Component implements QueryInterface public function addFacet($name, $type, $options) { $this->facets[$name] = [$type => $options]; - return $this; } @@ -450,7 +449,6 @@ class Query extends Component implements QueryInterface public function query($query) { $this->query = $query; - return $this; } @@ -462,7 +460,6 @@ class Query extends Component implements QueryInterface public function filter($filter) { $this->filter = $filter; - return $this; } @@ -479,7 +476,6 @@ class Query extends Component implements QueryInterface { $this->index = $index; $this->type = $type; - return $this; } @@ -496,7 +492,6 @@ class Query extends Component implements QueryInterface } else { $this->fields = func_get_args(); } - return $this; } @@ -510,7 +505,6 @@ class Query extends Component implements QueryInterface public function timeout($timeout) { $this->timeout = $timeout; - return $this; } } diff --git a/extensions/gii/generators/crud/Generator.php b/extensions/gii/generators/crud/Generator.php index dd7e6d1..e6ed347 100644 --- a/extensions/gii/generators/crud/Generator.php +++ b/extensions/gii/generators/crud/Generator.php @@ -400,10 +400,10 @@ class Generator extends \yii\gii\Generator case Schema::TYPE_TIME: case Schema::TYPE_DATETIME: case Schema::TYPE_TIMESTAMP: - $conditions[] = "\$this->addCondition(\$query, '{$column}');"; + $conditions[] = "\$query->andFilterWhere(['{$column}' => \$this->{$column}]);"; break; default: - $conditions[] = "\$this->addCondition(\$query, '{$column}', true);"; + $conditions[] = "\$query->andFilterWhere(['like', '{$column}', \$this->{$column}]);"; break; } } diff --git a/extensions/gii/generators/crud/default/search.php b/extensions/gii/generators/crud/default/search.php index c797907..2c5754f 100644 --- a/extensions/gii/generators/crud/default/search.php +++ b/extensions/gii/generators/crud/default/search.php @@ -70,23 +70,4 @@ class <?= $searchModelClass ?> extends Model return $dataProvider; } - - protected function addCondition($query, $attribute, $partialMatch = false) - { - if (($pos = strrpos($attribute, '.')) !== false) { - $modelAttribute = substr($attribute, $pos + 1); - } else { - $modelAttribute = $attribute; - } - - $value = $this->$modelAttribute; - if (trim($value) === '') { - return; - } - if ($partialMatch) { - $query->andWhere(['like', $attribute, $value]); - } else { - $query->andWhere([$attribute => $value]); - } - } } diff --git a/extensions/redis/CHANGELOG.md b/extensions/redis/CHANGELOG.md index ac85a2c..c597a73 100644 --- a/extensions/redis/CHANGELOG.md +++ b/extensions/redis/CHANGELOG.md @@ -6,6 +6,7 @@ Yii Framework 2 redis extension Change Log - Bug #1993: afterFind event in AR is now called after relations have been populated (cebe, creocoder) - Enh #1773: keyPrefix property of Session and Cache is not restricted to alnum characters anymore (cebe) +- Enh #2002: Added filterWhere() method to yii\redis\ActiveQuery to allow easy addition of search filter conditions by ignoring empty search fields (samdark, cebe) - Enh #2892: ActiveRecord dirty attributes are now reset after call to `afterSave()` so information about changed attributes is available in `afterSave`-event (cebe) - Chg #2281: Renamed `ActiveRecord::create()` to `populateRecord()` and changed signature. This method will not call instantiate() anymore (cebe) - Chg #2146: Removed `ActiveRelation` class and moved the functionality to `ActiveQuery`. diff --git a/extensions/sphinx/CHANGELOG.md b/extensions/sphinx/CHANGELOG.md index 2fed0ef..e83d64a 100644 --- a/extensions/sphinx/CHANGELOG.md +++ b/extensions/sphinx/CHANGELOG.md @@ -7,6 +7,7 @@ Yii Framework 2 sphinx extension Change Log - Bug #1993: afterFind event in AR is now called after relations have been populated (cebe, creocoder) - Bug #2160: SphinxQL does not support `OFFSET` (qiangxue, romeo7) - Enh #1398: Refactor ActiveRecord to use BaseActiveRecord class of the framework (klimov-paul) +- Enh #2002: Added filterWhere() method to yii\spinx\Query to allow easy addition of search filter conditions by ignoring empty search fields (samdark, cebe) - Enh #2892: ActiveRecord dirty attributes are now reset after call to `afterSave()` so information about changed attributes is available in `afterSave`-event (cebe) - Chg #2281: Renamed `ActiveRecord::create()` to `populateRecord()` and changed signature. This method will not call instantiate() anymore (cebe) - Chg #2146: Removed `ActiveRelation` class and moved the functionality to `ActiveQuery`. diff --git a/extensions/sphinx/Query.php b/extensions/sphinx/Query.php index ef7e5ea..d0988c0 100644 --- a/extensions/sphinx/Query.php +++ b/extensions/sphinx/Query.php @@ -466,7 +466,25 @@ class Query extends Component implements QueryInterface { $this->where = $condition; $this->addParams($params); + return $this; + } + /** + * Sets the WHERE part of the query ignoring empty parameters. + * + * @param string|array $condition the conditions that should be put in the WHERE part. Please refer to [[where()]] + * on how to specify this parameter. + * @param array $params the parameters (name => value) to be bound to the query. + * @return static the query object itself + * @see andFilter() + * @see orFilter() + */ + public function filterWhere($condition, $params = []) + { + $condition = $this->filterCondition($condition); + if ($condition !== []) { + $this->where($condition, $params); + } return $this; } @@ -488,7 +506,26 @@ class Query extends Component implements QueryInterface $this->where = ['and', $this->where, $condition]; } $this->addParams($params); + return $this; + } + /** + * Adds an additional WHERE condition to the existing one ignoring empty parameters. + * The new condition and the existing one will be joined using the 'AND' operator. + * + * @param string|array $condition the new WHERE condition. Please refer to [[where()]] + * on how to specify this parameter. + * @param array $params the parameters (name => value) to be bound to the query. + * @return static the query object itself + * @see filter() + * @see orFilter() + */ + public function andFilterWhere($condition, $params = []) + { + $condition = $this->filterCondition($condition); + if ($condition !== []) { + $this->andWhere($condition, $params); + } return $this; } @@ -510,7 +547,26 @@ class Query extends Component implements QueryInterface $this->where = ['or', $this->where, $condition]; } $this->addParams($params); + return $this; + } + /** + * Adds an additional WHERE condition to the existing one ignoring empty parameters. + * The new condition and the existing one will be joined using the 'OR' operator. + * + * @param string|array $condition the new WHERE condition. Please refer to [[where()]] + * on how to specify this parameter. + * @param array $params the parameters (name => value) to be bound to the query. + * @return static the query object itself + * @see filter() + * @see andFilter() + */ + public function orFilterWhere($condition, $params = []) + { + $condition = $this->filterCondition($condition); + if ($condition !== []) { + $this->orWhere($condition, $params); + } return $this; } diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index bf683d0..b4b6232 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -114,6 +114,7 @@ Yii Framework 2 Change Log - Enh #1921: Grid view ActionColumn now allow to name buttons like `{controller/action}` (creocoder) - Enh #1973: `yii message/extract` is now able to generate `.po` files (SergeiKutanov, samdark) - Enh #1984: ActionFilter will now mark event as handled when action run is aborted (cebe) +- Enh #2002: Added filterWhere() method to yii\db\Query to allow easy addition of search filter conditions by ignoring empty search fields (samdark, cebe) - Enh #2003: Added `filter` property to `ExistValidator` and `UniqueValidator` to support adding additional filtering conditions (qiangxue) - Enh #2008: `yii message/extract` is now able to save translation strings to database (kate-kate, samdark) - Enh #2043: Added support for custom request body parsers (danschmidt5189, cebe) diff --git a/framework/db/Query.php b/framework/db/Query.php index a29e988..57cac7e 100644 --- a/framework/db/Query.php +++ b/framework/db/Query.php @@ -526,7 +526,6 @@ class Query extends Component implements QueryInterface { $this->where = $condition; $this->addParams($params); - return $this; } @@ -570,7 +569,65 @@ class Query extends Component implements QueryInterface $this->where = ['or', $this->where, $condition]; } $this->addParams($params); + return $this; + } + /** + * Sets the WHERE part of the query ignoring empty parameters. + * + * @param string|array $condition the conditions that should be put in the WHERE part. Please refer to [[where()]] + * on how to specify this parameter. + * @param array $params the parameters (name => value) to be bound to the query. + * @return static the query object itself + * @see andFilter() + * @see orFilter() + */ + public function filterWhere($condition, $params = []) + { + $condition = $this->filterCondition($condition); + if ($condition !== []) { + $this->where($condition, $params); + } + return $this; + } + + /** + * Adds an additional WHERE condition to the existing one ignoring empty parameters. + * The new condition and the existing one will be joined using the 'AND' operator. + * + * @param string|array $condition the new WHERE condition. Please refer to [[where()]] + * on how to specify this parameter. + * @param array $params the parameters (name => value) to be bound to the query. + * @return static the query object itself + * @see filter() + * @see orFilter() + */ + public function andFilterWhere($condition, $params = []) + { + $condition = $this->filterCondition($condition); + if ($condition !== []) { + $this->andWhere($condition, $params); + } + return $this; + } + + /** + * Adds an additional WHERE condition to the existing one ignoring empty parameters. + * The new condition and the existing one will be joined using the 'OR' operator. + * + * @param string|array $condition the new WHERE condition. Please refer to [[where()]] + * on how to specify this parameter. + * @param array $params the parameters (name => value) to be bound to the query. + * @return static the query object itself + * @see filter() + * @see andFilter() + */ + public function orFilterWhere($condition, $params = []) + { + $condition = $this->filterCondition($condition); + if ($condition !== []) { + $this->orWhere($condition, $params); + } return $this; } diff --git a/framework/db/QueryInterface.php b/framework/db/QueryInterface.php index 090ce7c..7a1bedc 100644 --- a/framework/db/QueryInterface.php +++ b/framework/db/QueryInterface.php @@ -144,6 +144,17 @@ interface QueryInterface public function where($condition); /** + * Sets the WHERE part of the query ignoring empty parameters. + * + * @param array $condition the conditions that should be put in the WHERE part. Please refer to [[where()]] + * on how to specify this parameter. + * @return static the query object itself + * @see andFilterWhere() + * @see orFilterWhere() + */ + public function filterWhere($condition); + + /** * Adds an additional WHERE condition to the existing one. * The new condition and the existing one will be joined using the 'AND' operator. * @param string|array $condition the new WHERE condition. Please refer to [[where()]] @@ -155,6 +166,17 @@ interface QueryInterface public function andWhere($condition); /** + * Adds an additional WHERE condition to the existing one ignoring empty parameters. + * The new condition and the existing one will be joined using the 'AND' operator. + * @param string|array $condition the new WHERE condition. Please refer to [[where()]] + * on how to specify this parameter. + * @return static the query object itself + * @see filterWhere() + * @see orFilterWhere() + */ + public function andFilterWhere($condition); + + /** * Adds an additional WHERE condition to the existing one. * The new condition and the existing one will be joined using the 'OR' operator. * @param string|array $condition the new WHERE condition. Please refer to [[where()]] @@ -166,6 +188,17 @@ interface QueryInterface public function orWhere($condition); /** + * Adds an additional WHERE condition to the existing one ignoring empty parameters. + * The new condition and the existing one will be joined using the 'OR' operator. + * @param string|array $condition the new WHERE condition. Please refer to [[where()]] + * on how to specify this parameter. + * @return static the query object itself + * @see filterWhere() + * @see andFilterWhere() + */ + public function orFilterWhere($condition); + + /** * Sets the ORDER BY part of the query. * @param string|array $columns the columns (and the directions) to be ordered by. * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array diff --git a/framework/db/QueryTrait.php b/framework/db/QueryTrait.php index a2314f9..67e5a69 100644 --- a/framework/db/QueryTrait.php +++ b/framework/db/QueryTrait.php @@ -7,6 +7,8 @@ namespace yii\db; +use yii\base\NotSupportedException; + /** * The BaseQuery trait represents the minimum method set of a database Query. * @@ -126,6 +128,149 @@ trait QueryTrait } /** + * Sets the WHERE part of the query but ignores [[isParameterNotEmpty|empty parameters]]. + * + * This function can be used to pass fields of a search form directly as search condition + * by ignoring fields that have not been filled. + * + * @param array $condition the conditions that should be put in the WHERE part. + * See [[where()]] on how to specify this parameter. + * @return static the query object itself + * @see where() + * @see andFilterWhere() + * @see orFilterWhere() + */ + public function filterWhere($condition) + { + $condition = $this->filterCondition($condition); + if ($condition !== []) { + $this->where($condition); + } + return $this; + } + + /** + * Adds an additional WHERE condition to the existing one but ignores [[isParameterNotEmpty|empty parameters]]. + * The new condition and the existing one will be joined using the 'AND' operator. + * @param string|array $condition the new WHERE condition. Please refer to [[where()]] + * on how to specify this parameter. + * @return static the query object itself + * @see filterWhere() + * @see orFilterWhere() + */ + public function andFilterWhere($condition) + { + $condition = $this->filterCondition($condition); + if ($condition !== []) { + $this->andWhere($condition); + } + return $this; + } + + /** + * Adds an additional WHERE condition to the existing one but ignores [[isParameterNotEmpty|empty parameters]]. + * The new condition and the existing one will be joined using the 'OR' operator. + * @param string|array $condition the new WHERE condition. Please refer to [[where()]] + * on how to specify this parameter. + * @return static the query object itself + * @see filterWhere() + * @see andFilterWhere() + */ + public function orFilterWhere($condition) + { + $condition = $this->filterCondition($condition); + if ($condition !== []) { + $this->orWhere($condition); + } + return $this; + } + + /** + * Returns a new condition with [[isParameterNotEmpty|empty parameters]] removed. + * + * @param array $condition original condition + * @return array condition with [[isParameterNotEmpty|empty parameters]] removed. + * @throws NotSupportedException if the condition format is not supported + */ + protected function filterCondition($condition) + { + if (is_array($condition) && isset($condition[0])) { + $operator = strtoupper($condition[0]); + + switch ($operator) { + case 'NOT': + case 'AND': + case 'OR': + for ($i = 1, $operandsCount = count($condition); $i < $operandsCount; $i++) { + $subCondition = $this->filterCondition($condition[$i]); + if ($this->isParameterNotEmpty($subCondition)) { + $condition[$i] = $subCondition; + } else { + unset($condition[$i]); + } + } + + $operandsCount = count($condition) - 1; + if ($operator === 'NOT' && $operandsCount === 0) { + $condition = []; + } else { + // reindex array + array_splice($condition, 0, 0); + if ($operandsCount === 1) { + $condition = $condition[1]; + } + } + break; + case 'IN': + case 'NOT IN': + case 'LIKE': + case 'OR LIKE': + case 'NOT LIKE': + case 'OR NOT LIKE': + if (!$this->isParameterNotEmpty($condition[2])) { + $condition = []; + } + break; + case 'BETWEEN': + case 'NOT BETWEEN': + if (!$this->isParameterNotEmpty($condition[2]) && !$this->isParameterNotEmpty($condition[3])) { + $condition = []; + } + break; + default: + throw new NotSupportedException("filterWhere() does not support the '$operator' operator."); + } + } elseif (is_array($condition)) { + // hash format: 'column1' => 'value1', 'column2' => 'value2', ... + return array_filter($condition, [$this, 'isParameterNotEmpty']); + } else { + throw new NotSupportedException("filterWhere() does not support plain string conditions use where() instead."); + } + return $condition; + } + + /** + * Returns `true` if value passed is not "empty". + * + * The value is considered "empty", if + * + * - it is `null`, + * - an empty string (`''`), + * - a string containing only whitespace characters, + * - or an empty array. + * + * @param $value + * @return boolean if parameter is empty + */ + protected function isParameterNotEmpty($value) + { + if (is_string($value)) { + $value = trim($value); + } + return $value !== '' && $value !== [] && $value !== null; + } + + /** * Sets the ORDER BY part of the query. * @param string|array $columns the columns (and the directions) to be ordered by. * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array diff --git a/tests/unit/extensions/elasticsearch/QueryTest.php b/tests/unit/extensions/elasticsearch/QueryTest.php index 779dd2d..699db5f 100644 --- a/tests/unit/extensions/elasticsearch/QueryTest.php +++ b/tests/unit/extensions/elasticsearch/QueryTest.php @@ -151,6 +151,64 @@ class QueryTest extends ElasticSearchTestCase } + public function testFilterWhere() + { + // should work with hash format + $query = new Query; + $query->filterWhere([ + 'id' => 0, + 'title' => ' ', + 'author_ids' => [], + ]); + $this->assertEquals(['id' => 0], $query->where); + + $query->andFilterWhere(['status' => null]); + $this->assertEquals(['id' => 0], $query->where); + + $query->orFilterWhere(['name' => '']); + $this->assertEquals(['id' => 0], $query->where); + + // should work with operator format + $query = new Query; + $condition = ['like', 'name', 'Alex']; + $query->filterWhere($condition); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['between', 'id', null, null]); + $this->assertEquals($condition, $query->where); + + $query->orFilterWhere(['not between', 'id', null, null]); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['in', 'id', []]); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['not in', 'id', []]); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['not in', 'id', []]); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['like', 'id', '']); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['or like', 'id', '']); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['not like', 'id', ' ']); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['or not like', 'id', null]); + $this->assertEquals($condition, $query->where); + } + + public function testFilterWhereRecursively() + { + $query = new Query(); + $query->filterWhere(['and', ['like', 'name', ''], ['like', 'title', ''], ['id' => 1], ['not', ['like', 'name', '']]]); + $this->assertEquals(['id' => 1], $query->where); + } + // TODO test facets // TODO test complex where() every edge of QueryBuilder diff --git a/tests/unit/extensions/mongodb/QueryTest.php b/tests/unit/extensions/mongodb/QueryTest.php index d6f2da3..b628cfc 100644 --- a/tests/unit/extensions/mongodb/QueryTest.php +++ b/tests/unit/extensions/mongodb/QueryTest.php @@ -68,6 +68,24 @@ class QueryTest extends MongoDbTestCase ); } + public function testFilterWhere() + { + // should work with hash format + $query = new Query; + $query->filterWhere([ + 'id' => 0, + 'title' => ' ', + 'author_ids' => [], + ]); + $this->assertEquals(['id' => 0], $query->where); + + $query->andFilterWhere(['status' => null]); + $this->assertEquals(['id' => 0], $query->where); + + $query->orFilterWhere(['name' => '']); + $this->assertEquals(['id' => 0], $query->where); + } + public function testOrder() { $query = new Query; diff --git a/tests/unit/extensions/redis/ActiveRecordTest.php b/tests/unit/extensions/redis/ActiveRecordTest.php index 13a4348..76fd552 100644 --- a/tests/unit/extensions/redis/ActiveRecordTest.php +++ b/tests/unit/extensions/redis/ActiveRecordTest.php @@ -2,6 +2,7 @@ namespace yiiunit\extensions\redis; +use yii\redis\ActiveQuery; use yiiunit\data\ar\redis\ActiveRecord; use yiiunit\data\ar\redis\Customer; use yiiunit\data\ar\redis\OrderItem; @@ -239,4 +240,62 @@ class ActiveRecordTest extends RedisTestCase $this->assertNull(OrderItem::find($pk)); $this->assertNotNull(OrderItem::find(['order_id' => 2, 'item_id' => 10])); } + + public function testFilterWhere() + { + // should work with hash format + $query = new ActiveQuery(); + $query->filterWhere([ + 'id' => 0, + 'title' => ' ', + 'author_ids' => [], + ]); + $this->assertEquals(['id' => 0], $query->where); + + $query->andFilterWhere(['status' => null]); + $this->assertEquals(['id' => 0], $query->where); + + $query->orFilterWhere(['name' => '']); + $this->assertEquals(['id' => 0], $query->where); + + // should work with operator format + $query = new ActiveQuery(); + $condition = ['like', 'name', 'Alex']; + $query->filterWhere($condition); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['between', 'id', null, null]); + $this->assertEquals($condition, $query->where); + + $query->orFilterWhere(['not between', 'id', null, null]); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['in', 'id', []]); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['not in', 'id', []]); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['not in', 'id', []]); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['like', 'id', '']); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['or like', 'id', '']); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['not like', 'id', ' ']); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['or not like', 'id', null]); + $this->assertEquals($condition, $query->where); + } + + public function testFilterWhereRecursively() + { + $query = new ActiveQuery(); + $query->filterWhere(['and', ['like', 'name', ''], ['like', 'title', ''], ['id' => 1], ['not', ['like', 'name', '']]]); + $this->assertEquals(['id' => 1], $query->where); + } } diff --git a/tests/unit/extensions/sphinx/QueryTest.php b/tests/unit/extensions/sphinx/QueryTest.php index 0e0ddb1..815adb3 100644 --- a/tests/unit/extensions/sphinx/QueryTest.php +++ b/tests/unit/extensions/sphinx/QueryTest.php @@ -60,6 +60,70 @@ class QueryTest extends SphinxTestCase $this->assertEquals([':id' => 1, ':name' => 'something', ':age' => '30'], $query->params); } + public function testFilterWhere() + { + // should just call where() when string is passed + $query = new Query; + $query->filterWhere('id = :id', [':id' => null]); + $this->assertEquals('id = :id', $query->where); + $this->assertEquals([':id' => null], $query->params); + + // should work with hash format + $query = new Query; + $query->filterWhere([ + 'id' => 0, + 'title' => ' ', + 'author_ids' => [], + ]); + $this->assertEquals(['id' => 0], $query->where); + + $query->andFilterWhere(['status' => null]); + $this->assertEquals(['id' => 0], $query->where); + + $query->orFilterWhere(['name' => '']); + $this->assertEquals(['id' => 0], $query->where); + + // should work with operator format + $query = new Query; + $condition = ['like', 'name', 'Alex']; + $query->filterWhere($condition); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['between', 'id', null, null]); + $this->assertEquals($condition, $query->where); + + $query->orFilterWhere(['not between', 'id', null, null]); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['in', 'id', []]); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['not in', 'id', []]); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['not in', 'id', []]); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['like', 'id', '']); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['or like', 'id', '']); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['not like', 'id', ' ']); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['or not like', 'id', null]); + $this->assertEquals($condition, $query->where); + } + + public function testFilterWhereRecursively() + { + $query = new Query(); + $query->filterWhere(['and', ['like', 'name', ''], ['like', 'title', ''], ['id' => 1], ['not', ['like', 'name', '']]]); + $this->assertEquals(['id' => 1], $query->where); + } + public function testGroup() { $query = new Query; diff --git a/tests/unit/framework/db/QueryTest.php b/tests/unit/framework/db/QueryTest.php index 3039f04..16ce263 100644 --- a/tests/unit/framework/db/QueryTest.php +++ b/tests/unit/framework/db/QueryTest.php @@ -49,6 +49,70 @@ class QueryTest extends DatabaseTestCase $this->assertEquals([':id' => 1, ':name' => 'something', ':age' => '30'], $query->params); } + public function testFilterWhere() + { + // should just call where() when string is passed + $query = new Query; + $query->filterWhere('id = :id', [':id' => null]); + $this->assertEquals('id = :id', $query->where); + $this->assertEquals([':id' => null], $query->params); + + // should work with hash format + $query = new Query; + $query->filterWhere([ + 'id' => 0, + 'title' => ' ', + 'author_ids' => [], + ]); + $this->assertEquals(['id' => 0], $query->where); + + $query->andFilterWhere(['status' => null]); + $this->assertEquals(['id' => 0], $query->where); + + $query->orFilterWhere(['name' => '']); + $this->assertEquals(['id' => 0], $query->where); + + // should work with operator format + $query = new Query; + $condition = ['like', 'name', 'Alex']; + $query->filterWhere($condition); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['between', 'id', null, null]); + $this->assertEquals($condition, $query->where); + + $query->orFilterWhere(['not between', 'id', null, null]); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['in', 'id', []]); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['not in', 'id', []]); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['not in', 'id', []]); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['like', 'id', '']); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['or like', 'id', '']); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['not like', 'id', ' ']); + $this->assertEquals($condition, $query->where); + + $query->andFilterWhere(['or not like', 'id', null]); + $this->assertEquals($condition, $query->where); + } + + public function testFilterRecursively() + { + $query = new Query(); + $query->filterWhere(['and', ['like', 'name', ''], ['like', 'title', ''], ['id' => 1], ['not', ['like', 'name', '']]]); + $this->assertEquals(['id' => 1], $query->where); + } + public function testJoin() { }