Skip to content

Commit

Permalink
Fixes yiisoft#2315: Any operator now could be used with `yii\db\Query…
Browse files Browse the repository at this point in the history
…::->where()` operand format
  • Loading branch information
samdark committed Aug 1, 2014
1 parent d9a2397 commit c1cf48c
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 8 deletions.
13 changes: 13 additions & 0 deletions docs/guide/db-query-builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions framework/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
32 changes: 28 additions & 4 deletions framework/db/QueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -868,7 +868,6 @@ public function buildColumns($columns)
* 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)
{
Expand All @@ -882,11 +881,11 @@ public function buildCondition($condition, &$params)
$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);
}
Expand Down Expand Up @@ -1194,4 +1193,29 @@ public function buildExistsCondition($operator, $operands, &$params)
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";
}
}
34 changes: 30 additions & 4 deletions tests/unit/framework/db/QueryBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,36 @@ public function conditionProvider()
[ ['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
Expand Down

0 comments on commit c1cf48c

Please sign in to comment.