Commit c1cf48cf by Alexander Makarov

Fixes #2315: Any operator now could be used with `yii\db\Query::->where()` operand format

parent d9a23975
......@@ -249,6 +249,19 @@ Operator can be one of the following:
- `not exists`: similar to the `exists` operator and builds a `NOT EXISTS (sub-query)` expression.
Additionally you can specify anything as operator:
```php
$userQuery = (new Query)->select('id')->from('user');
$query->where(['>=', 'id', 10]);
```
It will result in:
```sql
SELECT id FROM user WHERE id > 10;
```
If you are building parts of condition dynamically it's very convenient to use `andWhere()` and `orWhere()`:
```php
......
......@@ -90,6 +90,7 @@ Yii Framework 2 Change Log
- Enh #1388: Added mapping from physical types to abstract types for OCI DB driver (qiangxue)
- Enh #1452: Added `Module::getInstance()` to allow accessing the module instance from anywhere within the module (qiangxue)
- Enh #2264: `CookieCollection::has()` will return false for expired or removed cookies (qiangxue)
- Enh #2315: Any operator now could be used with `yii\db\Query::->where()` operand format (samdark)
- Enh #2435: `yii\db\IntegrityException` is now thrown on database integrity errors instead of general `yii\db\Exception` (samdark)
- Enh #2558: Enhanced support for memcached by adding `yii\caching\MemCache::persistentId` and `yii\caching\MemCache::options` (qiangxue)
- Enh #2837: Error page now shows arguments in stack trace method calls (samdark)
......
......@@ -868,7 +868,6 @@ class QueryBuilder extends \yii\base\Object
* on how to specify a condition.
* @param array $params the binding parameters to be populated
* @return string the generated SQL expression
* @throws InvalidParamException if the condition is in bad format
*/
public function buildCondition($condition, &$params)
{
......@@ -882,11 +881,11 @@ class QueryBuilder extends \yii\base\Object
$operator = strtoupper($condition[0]);
if (isset($this->conditionBuilders[$operator])) {
$method = $this->conditionBuilders[$operator];
array_shift($condition);
return $this->$method($operator, $condition, $params);
} else {
throw new InvalidParamException('Found unknown operator in query: ' . $operator);
$method = 'buildSimpleCondition';
}
array_shift($condition);
return $this->$method($operator, $condition, $params);
} else { // hash format: 'column1' => 'value1', 'column2' => 'value2', ...
return $this->buildHashCondition($condition, $params);
}
......@@ -1194,4 +1193,29 @@ class QueryBuilder extends \yii\base\Object
throw new InvalidParamException('Subquery for EXISTS operator must be a Query object.');
}
}
/**
* Creates an SQL expressions like `"column" operator value`.
* @param string $operator the operator to use. Anything could be used e.g. `>`, `<=`, etc.
* @param array $operands contains two column names.
* @param array $params the binding parameters to be populated
* @return string the generated SQL expression
*/
public function buildSimpleCondition($operator, $operands, &$params)
{
if (!isset($operands[0], $operands[1])) {
throw new InvalidParamException("Operator '$operator' requires two operands.");
}
list($column, $value) = $operands;
if (strpos($column, '(') === false) {
$column = $this->db->quoteColumnName($column);
}
$phName = self::PARAM_PREFIX . 0;
$params[$phName] = $value === null ? 'NULL' : $value;
return "$column $operator $phName";
}
}
......@@ -152,10 +152,36 @@ class QueryBuilderTest extends DatabaseTestCase
[ ['or like', 'name', ['heyho', 'abc']], '"name" LIKE :qp0 OR "name" LIKE :qp1', [':qp0' => '%heyho%', ':qp1' => '%abc%'] ],
[ ['or not like', 'name', ['heyho', 'abc']], '"name" NOT LIKE :qp0 OR "name" NOT LIKE :qp1', [':qp0' => '%heyho%', ':qp1' => '%abc%'] ],
// TODO add more conditions
// IN
// NOT
// ...
// not
[ ['not', 'name'], 'NOT (name)', [] ],
// and
[ ['and', 'id=1', 'id=2'], '(id=1) AND (id=2)', [] ],
[ ['and', 'type=1', ['or', 'id=1', 'id=2']], '(type=1) AND ((id=1) OR (id=2))', [] ],
// or
[ ['or', 'id=1', 'id=2'], '(id=1) OR (id=2)', [] ],
[ ['or', 'type=1', ['or', 'id=1', 'id=2']], '(type=1) OR ((id=1) OR (id=2))', [] ],
// between
[ ['between', 'id', 1, 10], '"id" BETWEEN :qp0 AND :qp1', [':qp0' => 1, ':qp1' => 10] ],
[ ['not between', 'id', 1, 10], '"id" NOT BETWEEN :qp0 AND :qp1', [':qp0' => 1, ':qp1' => 10] ],
// in
[ ['in', 'id', [1, 2, 3]], '"id" IN (:qp0, :qp1, :qp2)', [':qp0' => 1, ':qp1' => 2, ':qp2' => 3] ],
[ ['not in', 'id', [1, 2, 3]], '"id" NOT IN (:qp0, :qp1, :qp2)', [':qp0' => 1, ':qp1' => 2, ':qp2' => 3] ],
// TODO: exists and not exists
// simple conditions
[ ['=', 'a', 'b'], '"a" = :qp0', [':qp0' => 'b'] ],
[ ['>', 'a', 1], '"a" > :qp0', [':qp0' => 1] ],
[ ['>=', 'a', 'b'], '"a" >= :qp0', [':qp0' => 'b'] ],
[ ['<', 'a', 2], '"a" < :qp0', [':qp0' => 2] ],
[ ['<=', 'a', 'b'], '"a" <= :qp0', [':qp0' => 'b'] ],
[ ['<>', 'a', 3], '"a" <> :qp0', [':qp0' => 3] ],
[ ['!=', 'a', 'b'], '"a" != :qp0', [':qp0' => 'b'] ],
];
// adjust dbms specific escaping
......
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