Commit 3c1a8141 by Alexander Makarov

fixes #2002

parent 19327ebd
...@@ -459,7 +459,7 @@ class Query extends Component implements QueryInterface ...@@ -459,7 +459,7 @@ class Query extends Component implements QueryInterface
* @param string $filter * @param string $filter
* @return static the query object itself * @return static the query object itself
*/ */
public function filter($filter) public function applyFilter($filter)
{ {
$this->filter = $filter; $this->filter = $filter;
......
...@@ -471,6 +471,26 @@ class Query extends Component implements QueryInterface ...@@ -471,6 +471,26 @@ class Query extends Component implements QueryInterface
} }
/** /**
* 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 filter($condition, $params = [])
{
$condition = $this->filterCondition($condition);
if ($condition !== []) {
$this->where($condition, $params);
}
return $this;
}
/**
* Adds an additional WHERE condition to the existing one. * Adds an additional WHERE condition to the existing one.
* The new condition and the existing one will be joined using the 'AND' operator. * 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()]] * @param string|array $condition the new WHERE condition. Please refer to [[where()]]
...@@ -493,6 +513,27 @@ class Query extends Component implements QueryInterface ...@@ -493,6 +513,27 @@ class Query extends Component implements QueryInterface
} }
/** /**
* 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 andFilter($condition, $params = [])
{
$condition = $this->filterCondition($condition);
if ($condition !== []) {
$this->andWhere($condition, $params);
}
return $this;
}
/**
* Adds an additional WHERE condition to the existing one. * Adds an additional WHERE condition to the existing one.
* The new condition and the existing one will be joined using the 'OR' operator. * 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()]] * @param string|array $condition the new WHERE condition. Please refer to [[where()]]
...@@ -515,6 +556,27 @@ class Query extends Component implements QueryInterface ...@@ -515,6 +556,27 @@ class Query extends Component implements QueryInterface
} }
/** /**
* 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 orFilter($condition, $params = [])
{
$condition = $this->filterCondition($condition);
if ($condition !== []) {
$this->orWhere($condition, $params);
}
return $this;
}
/**
* Sets the GROUP BY part of the query. * Sets the GROUP BY part of the query.
* @param string|array $columns the columns to be grouped by. * @param string|array $columns the columns to be grouped by.
* Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']). * Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']).
...@@ -744,4 +806,39 @@ class Query extends Component implements QueryInterface ...@@ -744,4 +806,39 @@ class Query extends Component implements QueryInterface
->callSnippets($from, $source, $match, $this->snippetOptions) ->callSnippets($from, $source, $match, $this->snippetOptions)
->queryColumn(); ->queryColumn();
} }
/**
* Returns new condition with empty (null, empty string, blank string, or empty array) parameters removed
*
* @param array $condition original condition
* @return array condition with empty parameters removed
*/
protected function filterCondition($condition)
{
if (is_array($condition) && isset($condition[0])) {
$operator = strtoupper($condition[0]);
switch ($operator) {
case 'IN':
case 'NOT IN':
case 'LIKE':
case 'OR LIKE':
case 'NOT LIKE':
case 'OR NOT LIKE':
if (!$this->parameterNotEmpty($condition[2])) {
$condition = [];
}
break;
case 'BETWEEN':
case 'NOT BETWEEN':
if (!$this->parameterNotEmpty($condition[2]) && !$this->parameterNotEmpty($condition[3])) {
$condition = [];
}
break;
}
} else {
$condition = $this->filterConditionHash($condition);
}
return $condition;
}
} }
...@@ -521,6 +521,26 @@ class Query extends Component implements QueryInterface ...@@ -521,6 +521,26 @@ class Query extends Component implements QueryInterface
} }
/** /**
* 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 filter($condition, $params = [])
{
$condition = $this->filterCondition($condition);
if ($condition !== []) {
$this->where($condition, $params);
}
return $this;
}
/**
* Adds an additional WHERE condition to the existing one. * Adds an additional WHERE condition to the existing one.
* The new condition and the existing one will be joined using the 'AND' operator. * 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()]] * @param string|array $condition the new WHERE condition. Please refer to [[where()]]
...@@ -543,6 +563,27 @@ class Query extends Component implements QueryInterface ...@@ -543,6 +563,27 @@ class Query extends Component implements QueryInterface
} }
/** /**
* 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 andFilter($condition, $params = [])
{
$condition = $this->filterCondition($condition);
if ($condition !== []) {
$this->andWhere($condition, $params);
}
return $this;
}
/**
* Adds an additional WHERE condition to the existing one. * Adds an additional WHERE condition to the existing one.
* The new condition and the existing one will be joined using the 'OR' operator. * 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()]] * @param string|array $condition the new WHERE condition. Please refer to [[where()]]
...@@ -565,6 +606,27 @@ class Query extends Component implements QueryInterface ...@@ -565,6 +606,27 @@ class Query extends Component implements QueryInterface
} }
/** /**
* 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 orFilter($condition, $params = [])
{
$condition = $this->filterCondition($condition);
if ($condition !== []) {
$this->orWhere($condition, $params);
}
return $this;
}
/**
* Appends a JOIN part to the query. * Appends a JOIN part to the query.
* The first parameter specifies what type of join it is. * The first parameter specifies what type of join it is.
* @param string $type the type of join, such as INNER JOIN, LEFT JOIN. * @param string $type the type of join, such as INNER JOIN, LEFT JOIN.
...@@ -821,4 +883,39 @@ class Query extends Component implements QueryInterface ...@@ -821,4 +883,39 @@ class Query extends Component implements QueryInterface
return $this; return $this;
} }
/**
* Returns new condition with empty (null, empty string, blank string, or empty array) parameters removed
*
* @param array $condition original condition
* @return array condition with empty parameters removed
*/
protected function filterCondition($condition)
{
if (is_array($condition) && isset($condition[0])) {
$operator = strtoupper($condition[0]);
switch ($operator) {
case 'IN':
case 'NOT IN':
case 'LIKE':
case 'OR LIKE':
case 'NOT LIKE':
case 'OR NOT LIKE':
if (!$this->parameterNotEmpty($condition[2])) {
$condition = [];
}
break;
case 'BETWEEN':
case 'NOT BETWEEN':
if (!$this->parameterNotEmpty($condition[2]) && !$this->parameterNotEmpty($condition[3])) {
$condition = [];
}
break;
}
} else {
$condition = $this->filterConditionHash($condition);
}
return $condition;
}
} }
...@@ -144,6 +144,17 @@ interface QueryInterface ...@@ -144,6 +144,17 @@ interface QueryInterface
public function where($condition); 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 andFilter()
* @see orFilter()
*/
public function filter($condition);
/**
* Adds an additional WHERE condition to the existing one. * Adds an additional WHERE condition to the existing one.
* The new condition and the existing one will be joined using the 'AND' operator. * 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()]] * @param string|array $condition the new WHERE condition. Please refer to [[where()]]
...@@ -155,6 +166,17 @@ interface QueryInterface ...@@ -155,6 +166,17 @@ interface QueryInterface
public function andWhere($condition); 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 filter()
* @see orFilter()
*/
public function andFilter($condition);
/**
* Adds an additional WHERE condition to the existing one. * Adds an additional WHERE condition to the existing one.
* The new condition and the existing one will be joined using the 'OR' operator. * 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()]] * @param string|array $condition the new WHERE condition. Please refer to [[where()]]
...@@ -166,6 +188,17 @@ interface QueryInterface ...@@ -166,6 +188,17 @@ interface QueryInterface
public function orWhere($condition); 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 filter()
* @see andFilter()
*/
public function orFilter($condition);
/**
* Sets the ORDER BY part of the query. * Sets the ORDER BY part of the query.
* @param string|array $columns the columns (and the directions) to be ordered by. * @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 * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
......
...@@ -89,6 +89,67 @@ trait QueryTrait ...@@ -89,6 +89,67 @@ trait QueryTrait
} }
/** /**
* Returns true if value passed is null, empty string, blank string, or empty array.
*
* @param $value
* @return boolean if parameter is empty
*/
protected function parameterNotEmpty($value)
{
if (is_string($value)) {
$value = trim($value);
}
return $value !== '' && $value !== [] && $value !== null;
}
/**
* Returns new condition with empty (null, empty string, blank string, or empty array) parameters in hash format
* removed
*
* @param array $condition original condition
* @return array condition with empty parameters removed
*/
protected function filterConditionHash($condition)
{
if (is_array($condition) && !isset($condition[0])) {
// hash format: 'column1' => 'value1', 'column2' => 'value2', ...
$condition = array_filter($condition, [$this, 'parameterNotEmpty']);
}
return $condition;
}
/**
* Returns new condition with empty (null, empty string, blank string, or empty array) parameters removed
*
* @param array $condition original condition
* @return array condition with empty parameters removed
*/
protected function filterCondition($condition)
{
return $this->filterConditionHash($condition);
}
/**
* Sets the WHERE part of the query ignoring empty parameters.
*
* See [[QueryInterface::where()]] for detailed documentation.
*
* @param array $condition the conditions that should be put in the WHERE part.
* @return static the query object itself
* @see andFilter()
* @see orFilter()
*/
public function filter($condition)
{
$condition = $this->filterCondition($condition);
if ($condition !== []) {
$this->where($condition);
}
return $this;
}
/**
* Adds an additional WHERE condition to the existing one. * Adds an additional WHERE condition to the existing one.
* The new condition and the existing one will be joined using the 'AND' operator. * 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()]] * @param string|array $condition the new WHERE condition. Please refer to [[where()]]
...@@ -109,6 +170,26 @@ trait QueryTrait ...@@ -109,6 +170,26 @@ trait QueryTrait
} }
/** /**
* 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 filter()
* @see orFilter()
*/
public function andFilter($condition)
{
$condition = $this->filterCondition($condition);
if ($condition !== []) {
$this->andWhere($condition);
}
return $this;
}
/**
* Adds an additional WHERE condition to the existing one. * Adds an additional WHERE condition to the existing one.
* The new condition and the existing one will be joined using the 'OR' operator. * 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()]] * @param string|array $condition the new WHERE condition. Please refer to [[where()]]
...@@ -129,6 +210,26 @@ trait QueryTrait ...@@ -129,6 +210,26 @@ trait QueryTrait
} }
/** /**
* 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 filter()
* @see andFilter()
*/
public function orFilter($condition)
{
$condition = $this->filterCondition($condition);
if ($condition !== []) {
$this->orWhere($condition);
}
return $this;
}
/**
* Sets the ORDER BY part of the query. * Sets the ORDER BY part of the query.
* @param string|array $columns the columns (and the directions) to be ordered by. * @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 * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
......
...@@ -153,31 +153,20 @@ class QueryTest extends ElasticSearchTestCase ...@@ -153,31 +153,20 @@ class QueryTest extends ElasticSearchTestCase
public function testFilter() public function testFilter()
{ {
// should work with hash format
$query = new Query; $query = new Query;
$query->filter('id = :id', [':id' => 1]); $query->filter([
$this->assertEquals('id = :id', $query->where); 'id' => 0,
$this->assertEquals([':id' => 1], $query->params); 'title' => ' ',
'author_ids' => [],
$query->andFilter('name = :name', [':name' => 'something']); ]);
$this->assertEquals(['and', 'id = :id', 'name = :name'], $query->where); $this->assertEquals(['id' => 0], $query->where);
$this->assertEquals([':id' => 1, ':name' => 'something'], $query->params);
$query->andFilter(['status' => null]);
$query->orFilter('age = :age', [':age' => '30']); $this->assertEquals(['id' => 0], $query->where);
$this->assertEquals(['or', ['and', 'id = :id', 'name = :name'], 'age = :age'], $query->where);
$this->assertEquals([':id' => 1, ':name' => 'something', ':age' => '30'], $query->params); $query->orFilter(['name' => '']);
$this->assertEquals(['id' => 0], $query->where);
$query = new Query;
$query->filter('id = :id', [':id' => '']);
$this->assertEquals('', $query->where);
$this->assertEquals([], $query->params);
$query->andFilter('name = :name', [':name' => '']);
$this->assertEquals('', $query->where);
$this->assertEquals([], $query->params);
$query->orFilter('age = :age', [':age' => '']);
$this->assertEquals('', $query->where);
$this->assertEquals([], $query->params);
} }
// TODO test facets // TODO test facets
......
...@@ -70,44 +70,20 @@ class QueryTest extends MongoDbTestCase ...@@ -70,44 +70,20 @@ class QueryTest extends MongoDbTestCase
public function testFilter() public function testFilter()
{ {
// should work with hash format
$query = new Query; $query = new Query;
$query->filter(['name' => 'name1']); $query->filter([
$this->assertEquals(['name' => 'name1'], $query->where); 'id' => 0,
'title' => ' ',
$query->andFilter(['address' => 'address1']); 'author_ids' => [],
$this->assertEquals( ]);
[ $this->assertEquals(['id' => 0], $query->where);
'and',
['name' => 'name1'],
['address' => 'address1']
],
$query->where
);
$query->orFilter(['name' => 'name2']);
$this->assertEquals(
[
'or',
[
'and',
['name' => 'name1'],
['address' => 'address1']
],
['name' => 'name2']
],
$query->where
);
$query = new Query;
$query->filter(['name' => '']);
$this->assertEquals('', $query->where);
$query->andFilter(['address' => '']); $query->andFilter(['status' => null]);
$this->assertEquals([], $query->where); $this->assertEquals(['id' => 0], $query->where);
$query->orFilter(['name' => '']); $query->orFilter(['name' => '']);
$this->assertEquals([], $query->where); $this->assertEquals(['id' => 0], $query->where);
} }
public function testOrder() public function testOrder()
......
...@@ -62,31 +62,59 @@ class QueryTest extends SphinxTestCase ...@@ -62,31 +62,59 @@ class QueryTest extends SphinxTestCase
public function testFilter() public function testFilter()
{ {
// should just call where() when string is passed
$query = new Query; $query = new Query;
$query->filter('id = :id', [':id' => 1]); $query->filter('id = :id', [':id' => null]);
$this->assertEquals('id = :id', $query->where); $this->assertEquals('id = :id', $query->where);
$this->assertEquals([':id' => 1], $query->params); $this->assertEquals([':id' => null], $query->params);
$query->andFilter('name = :name', [':name' => 'something']); // should work with hash format
$this->assertEquals(['and', 'id = :id', 'name = :name'], $query->where); $query = new Query;
$this->assertEquals([':id' => 1, ':name' => 'something'], $query->params); $query->filter([
'id' => 0,
'title' => ' ',
'author_ids' => [],
]);
$this->assertEquals(['id' => 0], $query->where);
$query->orFilter('age = :age', [':age' => '30']); $query->andFilter(['status' => null]);
$this->assertEquals(['or', ['and', 'id = :id', 'name = :name'], 'age = :age'], $query->where); $this->assertEquals(['id' => 0], $query->where);
$this->assertEquals([':id' => 1, ':name' => 'something', ':age' => '30'], $query->params);
$query->orFilter(['name' => '']);
$this->assertEquals(['id' => 0], $query->where);
// should work with operator format
$query = new Query; $query = new Query;
$query->filter('id = :id', [':id' => '']); $condition = ['like', 'name', 'Alex'];
$this->assertEquals('', $query->where); $query->filter($condition);
$this->assertEquals([], $query->params); $this->assertEquals($condition, $query->where);
$query->andFilter(['between', 'id', null, null]);
$this->assertEquals($condition, $query->where);
$query->orFilter(['not between', 'id', null, null]);
$this->assertEquals($condition, $query->where);
$query->andFilter(['in', 'id', []]);
$this->assertEquals($condition, $query->where);
$query->andFilter(['not in', 'id', []]);
$this->assertEquals($condition, $query->where);
$query->andFilter(['not in', 'id', []]);
$this->assertEquals($condition, $query->where);
$query->andFilter(['like', 'id', '']);
$this->assertEquals($condition, $query->where);
$query->andFilter(['or like', 'id', '']);
$this->assertEquals($condition, $query->where);
$query->andFilter('name = :name', [':name' => '']); $query->andFilter(['not like', 'id', ' ']);
$this->assertEquals('', $query->where); $this->assertEquals($condition, $query->where);
$this->assertEquals([], $query->params);
$query->orFilter('age = :age', [':age' => '']); $query->andFilter(['or not like', 'id', null]);
$this->assertEquals('', $query->where); $this->assertEquals($condition, $query->where);
$this->assertEquals([], $query->params);
} }
public function testGroup() public function testGroup()
......
...@@ -51,32 +51,59 @@ class QueryTest extends DatabaseTestCase ...@@ -51,32 +51,59 @@ class QueryTest extends DatabaseTestCase
public function testFilter() public function testFilter()
{ {
// should just call where() when string is passed
$query = new Query; $query = new Query;
$query->filter('id = :id', [':id' => 1]); $query->filter('id = :id', [':id' => null]);
$this->assertEquals('id = :id', $query->where); $this->assertEquals('id = :id', $query->where);
$this->assertEquals([':id' => 1], $query->params); $this->assertEquals([':id' => null], $query->params);
$query->andFilter('name = :name', [':name' => 'something']); // should work with hash format
$this->assertEquals(['and', 'id = :id', 'name = :name'], $query->where); $query = new Query;
$this->assertEquals([':id' => 1, ':name' => 'something'], $query->params); $query->filter([
'id' => 0,
'title' => ' ',
'author_ids' => [],
]);
$this->assertEquals(['id' => 0], $query->where);
$query->orFilter('age = :age', [':age' => '30']); $query->andFilter(['status' => null]);
$this->assertEquals(['or', ['and', 'id = :id', 'name = :name'], 'age = :age'], $query->where); $this->assertEquals(['id' => 0], $query->where);
$this->assertEquals([':id' => 1, ':name' => 'something', ':age' => '30'], $query->params);
$query->orFilter(['name' => '']);
$this->assertEquals(['id' => 0], $query->where);
// should work with operator format
$query = new Query;
$condition = ['like', 'name', 'Alex'];
$query->filter($condition);
$this->assertEquals($condition, $query->where);
$query->andFilter(['between', 'id', null, null]);
$this->assertEquals($condition, $query->where);
$query->orFilter(['not between', 'id', null, null]);
$this->assertEquals($condition, $query->where);
$query->andFilter(['in', 'id', []]);
$this->assertEquals($condition, $query->where);
$query->andFilter(['not in', 'id', []]);
$this->assertEquals($condition, $query->where);
$query->andFilter(['not in', 'id', []]);
$this->assertEquals($condition, $query->where);
$query = new Query(); $query->andFilter(['like', 'id', '']);
$query->filter('id = :id', [':id' => '']); $this->assertEquals($condition, $query->where);
$this->assertEquals('', $query->where);
$this->assertEquals([], $query->params);
$query->andFilter('name = :name', [':name' => '']); $query->andFilter(['or like', 'id', '']);
$this->assertEquals('', $query->where); $this->assertEquals($condition, $query->where);
$this->assertEquals([], $query->params);
$query->orFilter('age = :age', [':age' => '']); $query->andFilter(['not like', 'id', ' ']);
$this->assertEquals('', $query->where); $this->assertEquals($condition, $query->where);
$this->assertEquals([], $query->params);
$query->andFilter(['or not like', 'id', null]);
$this->assertEquals($condition, $query->where);
} }
public function testJoin() public function testJoin()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment