mirror of
https://github.com/yiisoft/yii2.git
synced 2025-11-08 00:47:55 +08:00
Merge pull request #15643 from SilverFire/make-query-expression
Implement ExpressionInterface in Query
This commit is contained in:
@ -47,7 +47,7 @@ use yii\base\InvalidConfigException;
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Query extends Component implements QueryInterface
|
||||
class Query extends Component implements QueryInterface, ExpressionInterface
|
||||
{
|
||||
use QueryTrait;
|
||||
|
||||
|
||||
@ -161,6 +161,7 @@ class QueryBuilder extends \yii\base\BaseObject
|
||||
protected function defaultExpressionBuilders()
|
||||
{
|
||||
return [
|
||||
'yii\db\Query' => 'yii\db\QueryExpressionBuilder',
|
||||
'yii\db\PdoValue' => 'yii\db\PdoValueBuilder',
|
||||
'yii\db\Expression' => 'yii\db\ExpressionBuilder',
|
||||
'yii\db\conditions\ConjunctionCondition' => 'yii\db\conditions\ConjunctionConditionBuilder',
|
||||
@ -287,6 +288,10 @@ class QueryBuilder extends \yii\base\BaseObject
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->expressionBuilders[$className] === __CLASS__) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (!is_object($this->expressionBuilders[$className])) {
|
||||
$this->expressionBuilders[$className] = new $this->expressionBuilders[$className]($this);
|
||||
}
|
||||
|
||||
30
framework/db/QueryExpressionBuilder.php
Normal file
30
framework/db/QueryExpressionBuilder.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* Class QueryExpressionBuilder is used internally to build [[Query]] object
|
||||
* using unified [[QueryBuilder]] expression building interface.
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class QueryExpressionBuilder implements ExpressionBuilderInterface
|
||||
{
|
||||
use ExpressionBuilderTrait;
|
||||
|
||||
/**
|
||||
* Method builds the raw SQL from the $expression that will not be additionally
|
||||
* escaped or quoted.
|
||||
*
|
||||
* @param ExpressionInterface|Query $expression the expression to be built.
|
||||
* @param array $params the binding parameters.
|
||||
* @return string the raw SQL that will not be additionally escaped or quoted.
|
||||
*/
|
||||
public function build(ExpressionInterface $expression, array &$params = [])
|
||||
{
|
||||
list($sql, $params) = $this->queryBuilder->build($expression, $params);
|
||||
|
||||
return "($sql)";
|
||||
}
|
||||
}
|
||||
@ -15,11 +15,11 @@ class BetweenCondition implements ConditionInterface
|
||||
/**
|
||||
* @var string $operator the operator to use (e.g. `BETWEEN` or `NOT BETWEEN`)
|
||||
*/
|
||||
protected $operator;
|
||||
private $operator;
|
||||
/**
|
||||
* @var mixed the column name to the left of [[operator]]
|
||||
*/
|
||||
protected $column;
|
||||
private $column;
|
||||
/**
|
||||
* @var mixed beginning of the interval
|
||||
*/
|
||||
|
||||
@ -29,8 +29,8 @@ class ExistsConditionBuilder implements ExpressionBuilderInterface
|
||||
$operator = $expression->getOperator();
|
||||
$query = $expression->getQuery();
|
||||
|
||||
list($sql, $params) = $this->queryBuilder->build($query, $params);
|
||||
$sql = $this->queryBuilder->buildExpression($query, $params);
|
||||
|
||||
return "$operator ($sql)";
|
||||
return "$operator $sql";
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
namespace yii\db\conditions;
|
||||
|
||||
use yii\base\InvalidArgumentException;
|
||||
use yii\db\ExpressionInterface;
|
||||
|
||||
/**
|
||||
* Class InCondition represents `IN` condition.
|
||||
@ -15,20 +16,20 @@ class InCondition implements ConditionInterface
|
||||
/**
|
||||
* @var string $operator the operator to use (e.g. `IN` or `NOT IN`)
|
||||
*/
|
||||
protected $operator;
|
||||
private $operator;
|
||||
|
||||
/**
|
||||
* @var string|string[] the column name. If it is an array, a composite `IN` condition
|
||||
* will be generated.
|
||||
*/
|
||||
protected $column;
|
||||
private $column;
|
||||
|
||||
/**
|
||||
* @var array an array of values that [[column]] value should be among.
|
||||
* @var ExpressionInterface[]|string[]|int[] an array of values that [[column]] value should be among.
|
||||
* If it is an empty array the generated expression will be a `false` value if
|
||||
* [[operator]] is `IN` and empty if operator is `NOT IN`.
|
||||
*/
|
||||
protected $values;
|
||||
private $values;
|
||||
|
||||
/**
|
||||
* SimpleCondition constructor
|
||||
@ -63,7 +64,7 @@ class InCondition implements ConditionInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
* @return ExpressionInterface[]|string[]|int[]
|
||||
*/
|
||||
public function getValues()
|
||||
{
|
||||
|
||||
@ -109,7 +109,8 @@ class InConditionBuilder implements ExpressionBuilderInterface
|
||||
*/
|
||||
protected function buildSubqueryInCondition($operator, $columns, $values, &$params)
|
||||
{
|
||||
list($sql, $params) = $this->queryBuilder->build($values, $params);
|
||||
$sql = $this->queryBuilder->buildExpression($values, $params);
|
||||
|
||||
if (is_array($columns)) {
|
||||
foreach ($columns as $i => $col) {
|
||||
if (strpos($col, '(') === false) {
|
||||
@ -117,14 +118,14 @@ class InConditionBuilder implements ExpressionBuilderInterface
|
||||
}
|
||||
}
|
||||
|
||||
return '(' . implode(', ', $columns) . ") $operator ($sql)";
|
||||
return '(' . implode(', ', $columns) . ") $operator $sql";
|
||||
}
|
||||
|
||||
if (strpos($columns, '(') === false) {
|
||||
$columns = $this->queryBuilder->db->quoteColumnName($columns);
|
||||
}
|
||||
|
||||
return "$columns $operator ($sql)";
|
||||
return "$columns $operator $sql";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -15,7 +15,7 @@ class NotCondition implements ConditionInterface
|
||||
/**
|
||||
* @var mixed the condition to be negated
|
||||
*/
|
||||
protected $condition;
|
||||
private $condition;
|
||||
|
||||
/**
|
||||
* NotCondition constructor.
|
||||
|
||||
@ -15,15 +15,15 @@ class SimpleCondition implements ConditionInterface
|
||||
/**
|
||||
* @var string $operator the operator to use. Anything could be used e.g. `>`, `<=`, etc.
|
||||
*/
|
||||
protected $operator;
|
||||
private $operator;
|
||||
/**
|
||||
* @var mixed the column name to the left of [[operator]]
|
||||
*/
|
||||
protected $column;
|
||||
private $column;
|
||||
/**
|
||||
* @var mixed the value to the right of the [[operator]]
|
||||
*/
|
||||
protected $value;
|
||||
private $value;
|
||||
|
||||
/**
|
||||
* SimpleCondition constructor
|
||||
|
||||
@ -41,10 +41,6 @@ class SimpleConditionBuilder implements ExpressionBuilderInterface
|
||||
if ($value instanceof ExpressionInterface) {
|
||||
return "$column $operator {$this->queryBuilder->buildExpression($value, $params)}";
|
||||
}
|
||||
if ($value instanceof Query) {
|
||||
list($sql, $params) = $this->queryBuilder->build($value, $params);
|
||||
return "$column $operator ($sql)";
|
||||
}
|
||||
|
||||
$phName = $this->queryBuilder->bindParam($value, $params);
|
||||
return "$column $operator $phName";
|
||||
|
||||
@ -1092,18 +1092,19 @@ abstract class QueryBuilderTest extends DatabaseTestCase
|
||||
|
||||
// not
|
||||
[['not', 'name'], 'NOT (name)', []],
|
||||
[['not', (new Query)->select('exists')->from('some_table')], 'NOT ((SELECT [[exists]] FROM [[some_table]]))', []],
|
||||
|
||||
// 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))', []],
|
||||
[['and', 'id=1', new Expression('id=:qp0', [':qp0' => 2])], '(id=1) AND (id=:qp0)', [':qp0' => 2]],
|
||||
[['and', ['expired' => false], (new Query)->select('count(*) > 1')->from('queue')], '([[expired]]=:qp0) AND ((SELECT count(*) > 1 FROM [[queue]]))', [':qp0' => false]],
|
||||
|
||||
// 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))', []],
|
||||
[['or', 'type=1', new Expression('id=:qp0', [':qp0' => 1])], '(type=1) OR (id=:qp0)', [':qp0' => 1]],
|
||||
|
||||
|
||||
// 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]],
|
||||
@ -1118,7 +1119,7 @@ abstract class QueryBuilderTest extends DatabaseTestCase
|
||||
[new BetweenColumnsCondition(new Expression('NOW()'), 'NOT BETWEEN', (new Query)->select('min_date')->from('some_table'), 'max_date'), 'NOW() NOT BETWEEN (SELECT [[min_date]] FROM [[some_table]]) AND [[max_date]]', []],
|
||||
|
||||
// in
|
||||
[['in', 'id', [1, 2, 3]], '[[id]] IN (:qp0, :qp1, :qp2)', [':qp0' => 1, ':qp1' => 2, ':qp2' => 3]],
|
||||
[['in', 'id', [1, 2, (new Query())->select('three')->from('digits')]], '[[id]] IN (:qp0, :qp1, (SELECT [[three]] FROM [[digits]]))', [':qp0' => 1, ':qp1' => 2]],
|
||||
[['not in', 'id', [1, 2, 3]], '[[id]] NOT IN (:qp0, :qp1, :qp2)', [':qp0' => 1, ':qp1' => 2, ':qp2' => 3]],
|
||||
[['in', 'id', (new Query())->select('id')->from('users')->where(['active' => 1])], '[[id]] IN (SELECT [[id]] FROM [[users]] WHERE [[active]]=:qp0)', [':qp0' => 1]],
|
||||
[['not in', 'id', (new Query())->select('id')->from('users')->where(['active' => 1])], '[[id]] NOT IN (SELECT [[id]] FROM [[users]] WHERE [[active]]=:qp0)', [':qp0' => 1]],
|
||||
|
||||
Reference in New Issue
Block a user