Commit 1978a4ef by Qiang Xue

...

parent e5534b3c
...@@ -358,25 +358,25 @@ class YiiBase ...@@ -358,25 +358,25 @@ class YiiBase
$class = static::import($class, true); $class = static::import($class, true);
} }
if (($n = func_num_args()-1) > 0) { if (($n = func_num_args()) > 1) {
$args = func_get_args(); $args = func_get_args();
array_shift($args); // remove $config if ($n === 2) {
} $object = new $class($args[1]);
if ($n === 0) {
$object = new $class;
} elseif ($n === 1) {
$object = new $class($args[0]);
} elseif ($n === 2) {
$object = new $class($args[0], $args[1]);
} elseif ($n === 3) { } elseif ($n === 3) {
$object = new $class($args[0], $args[1], $args[2]); $object = new $class($args[1], $args[2]);
} elseif ($n === 4) {
$object = new $class($args[1], $args[2], $args[3]);
} else { } else {
array_shift($args); // remove $config
$r = new \ReflectionClass($class); $r = new \ReflectionClass($class);
$object = $r->newInstanceArgs($args); $object = $r->newInstanceArgs($args);
} }
} else {
$object = new $class;
}
$class = '\\' . get_class($object); $class = '\\' . get_class($object);
if (isset(\Yii::$objectConfig[$class])) { if (isset(\Yii::$objectConfig[$class])) {
$config = array_merge(\Yii::$objectConfig[$class], $config); $config = array_merge(\Yii::$objectConfig[$class], $config);
} }
......
...@@ -302,23 +302,22 @@ class Object ...@@ -302,23 +302,22 @@ class Object
{ {
$class = '\\' . get_called_class(); $class = '\\' . get_called_class();
if (($n = func_num_args()-1) > 0) { if (($n = func_num_args()) > 1) {
$args = func_get_args(); $args = func_get_args();
array_shift($args); // remove $config if ($n === 2) {
} $object = new $class($args[1]);
if ($n === 0) {
$object = new $class;
} elseif ($n === 1) {
$object = new $class($args[0]);
} elseif ($n === 2) {
$object = new $class($args[0], $args[1]);
} elseif ($n === 3) { } elseif ($n === 3) {
$object = new $class($args[0], $args[1], $args[2]); $object = new $class($args[1], $args[2]);
} elseif ($n === 4) {
$object = new $class($args[1], $args[2], $args[3]);
} else { } else {
array_shift($args); // remove $config
$r = new \ReflectionClass($class); $r = new \ReflectionClass($class);
$object = $r->newInstanceArgs($args); $object = $r->newInstanceArgs($args);
} }
} else {
$object = new $class;
}
if (isset(\Yii::$objectConfig[$class])) { if (isset(\Yii::$objectConfig[$class])) {
$config = array_merge(\Yii::$objectConfig[$class], $config); $config = array_merge(\Yii::$objectConfig[$class], $config);
......
...@@ -10,8 +10,8 @@ ...@@ -10,8 +10,8 @@
namespace yii\db\ar; namespace yii\db\ar;
use yii\db\dao\BaseQuery;
use yii\base\VectorIterator; use yii\base\VectorIterator;
use yii\db\dao\Query;
/** /**
* ActiveFinder.php is ... * ActiveFinder.php is ...
...@@ -20,31 +20,45 @@ use yii\base\VectorIterator; ...@@ -20,31 +20,45 @@ use yii\base\VectorIterator;
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class ActiveQuery extends BaseQuery implements \IteratorAggregate, \ArrayAccess, \Countable class ActiveQuery extends \yii\base\Object implements \IteratorAggregate, \ArrayAccess, \Countable
{ {
public $modelClass; public $modelClass;
/**
* @var \yii\db\dao\Query the Query object
*/
public $query;
public $with; public $with;
public $alias; public $tableAlias;
public $indexBy; public $indexBy;
public $asArray; public $asArray;
public $scopes;
public $records; public $records;
public $sql; public $sql;
public function __construct($modelClass)
{
$this->modelClass = $modelClass;
$this->query = new Query;
}
public function all($refresh = false) public function all($refresh = false)
{ {
if ($this->records === null || $refresh) { if ($this->records === null || $refresh) {
$this->records = $this->performQuery(); $this->records = $this->findRecords();
} }
return $this->records; return $this->records;
} }
public function one($refresh = false) public function one($refresh = false, $limitOne = true)
{ {
if ($this->records === null || $refresh) { if ($this->records === null || $refresh) {
$this->limit = 1; if ($limitOne) {
$this->records = $this->performQuery(); $this->limit(1);
}
$this->records = $this->findRecords();
} }
if (isset($this->records[0])) { if (isset($this->records[0])) {
return $this->records[0]; return $this->records[0];
...@@ -53,6 +67,11 @@ class ActiveQuery extends BaseQuery implements \IteratorAggregate, \ArrayAccess, ...@@ -53,6 +67,11 @@ class ActiveQuery extends BaseQuery implements \IteratorAggregate, \ArrayAccess,
} }
} }
public function exists()
{
}
public function asArray($value = true) public function asArray($value = true)
{ {
$this->asArray = $value; $this->asArray = $value;
...@@ -71,9 +90,9 @@ class ActiveQuery extends BaseQuery implements \IteratorAggregate, \ArrayAccess, ...@@ -71,9 +90,9 @@ class ActiveQuery extends BaseQuery implements \IteratorAggregate, \ArrayAccess,
return $this; return $this;
} }
public function alias($tableAlias) public function tableAlias($value)
{ {
$this->alias = $tableAlias; $this->tableAlias = $value;
return $this; return $this;
} }
...@@ -123,7 +142,7 @@ class ActiveQuery extends BaseQuery implements \IteratorAggregate, \ArrayAccess, ...@@ -123,7 +142,7 @@ class ActiveQuery extends BaseQuery implements \IteratorAggregate, \ArrayAccess,
public function getIterator() public function getIterator()
{ {
if ($this->records === null) { if ($this->records === null) {
$this->records = $this->performQuery(); $this->records = $this->findRecords();
} }
return new VectorIterator($this->records); return new VectorIterator($this->records);
} }
...@@ -142,7 +161,7 @@ class ActiveQuery extends BaseQuery implements \IteratorAggregate, \ArrayAccess, ...@@ -142,7 +161,7 @@ class ActiveQuery extends BaseQuery implements \IteratorAggregate, \ArrayAccess,
return $this->performCountQuery(); return $this->performCountQuery();
} else { } else {
if ($this->records === null) { if ($this->records === null) {
$this->records = $this->performQuery(); $this->records = $this->findRecords();
} }
return count($this->records); return count($this->records);
} }
...@@ -158,7 +177,7 @@ class ActiveQuery extends BaseQuery implements \IteratorAggregate, \ArrayAccess, ...@@ -158,7 +177,7 @@ class ActiveQuery extends BaseQuery implements \IteratorAggregate, \ArrayAccess,
public function offsetExists($offset) public function offsetExists($offset)
{ {
if ($this->records === null) { if ($this->records === null) {
$this->records = $this->performQuery(); $this->records = $this->findRecords();
} }
return isset($this->records[$offset]); return isset($this->records[$offset]);
} }
...@@ -175,7 +194,7 @@ class ActiveQuery extends BaseQuery implements \IteratorAggregate, \ArrayAccess, ...@@ -175,7 +194,7 @@ class ActiveQuery extends BaseQuery implements \IteratorAggregate, \ArrayAccess,
public function offsetGet($offset) public function offsetGet($offset)
{ {
if ($this->records === null) { if ($this->records === null) {
$this->records = $this->performQuery(); $this->records = $this->findRecords();
} }
return isset($this->records[$offset]) ? $this->records[$offset] : null; return isset($this->records[$offset]) ? $this->records[$offset] : null;
} }
...@@ -194,7 +213,7 @@ class ActiveQuery extends BaseQuery implements \IteratorAggregate, \ArrayAccess, ...@@ -194,7 +213,7 @@ class ActiveQuery extends BaseQuery implements \IteratorAggregate, \ArrayAccess,
public function offsetSet($offset, $item) public function offsetSet($offset, $item)
{ {
if ($this->records === null) { if ($this->records === null) {
$this->records = $this->performQuery(); $this->records = $this->findRecords();
} }
$this->records[$offset] = $item; $this->records[$offset] = $item;
} }
...@@ -210,37 +229,502 @@ class ActiveQuery extends BaseQuery implements \IteratorAggregate, \ArrayAccess, ...@@ -210,37 +229,502 @@ class ActiveQuery extends BaseQuery implements \IteratorAggregate, \ArrayAccess,
public function offsetUnset($offset) public function offsetUnset($offset)
{ {
if ($this->records === null) { if ($this->records === null) {
$this->records = $this->performQuery(); $this->records = $this->findRecords();
} }
unset($this->records[$offset]); unset($this->records[$offset]);
} }
protected function performQuery() /**
* Sets the SELECT part of the query.
* @param mixed $columns the columns to be selected. Defaults to '*', meaning all columns.
* Columns can be specified in either a string (e.g. "id, name") or an array (e.g. array('id', 'name')).
* Columns can contain table prefixes (e.g. "tbl_user.id") and/or column aliases (e.g. "tbl_user.id AS user_id").
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @param string $option additional option that should be appended to the 'SELECT' keyword. For example,
* in MySQL, the option 'SQL_CALC_FOUND_ROWS' can be used.
* @return BaseQuery the query object itself
*/
public function select($columns = '*', $option = '')
{
$this->query->select($columns, $option);
return $this;
}
/**
* Sets the value indicating whether to SELECT DISTINCT or not.
* @param bool $value whether to SELECT DISTINCT or not.
* @return BaseQuery the query object itself
*/
public function distinct($value = true)
{
$this->query->distinct($value);
return $this;
}
/**
* Sets the FROM part of the query.
* @param mixed $tables the table(s) to be selected from. This can be either a string (e.g. 'tbl_user')
* or an array (e.g. array('tbl_user', 'tbl_profile')) specifying one or several table names.
* Table names can contain schema prefixes (e.g. 'public.tbl_user') and/or table aliases (e.g. 'tbl_user u').
* The method will automatically quote the table names unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
* @return BaseQuery the query object itself
*/
public function from($tables)
{
$this->query->from($tables);
return $this;
}
/**
* Sets the WHERE part of the query.
*
* The method requires a $condition parameter, and optionally a $params parameter
* specifying the values to be bound to the query.
*
* The $condition parameter should be either a string (e.g. 'id=1') or an array.
* If the latter, it must be in one of the following two formats:
*
* - hash format: `array('column1' => value1, 'column2' => value2, ...)`
* - operator format: `array(operator, operand1, operand2, ...)`
*
* A condition in hash format represents the following SQL expression in general:
* `column1=value1 AND column2=value2 AND ...`. In case when a value is an array,
* an `IN` expression will be generated. And if a value is null, `IS NULL` will be used
* in the generated expression. Below are some examples:
*
* - `array('type'=>1, 'status'=>2)` generates `(type=1) AND (status=2)`.
* - `array('id'=>array(1,2,3), 'status'=>2)` generates `(id IN (1,2,3)) AND (status=2)`.
* - `array('status'=>null) generates `status IS NULL`.
*
* A condition in operator format generates the SQL expression according to the specified operator, which
* can be one of the followings:
*
* - `and`: the operands should be concatenated together using `AND`. For example,
* `array('and', 'id=1', 'id=2')` will generate `id=1 AND id=2`. If an operand is an array,
* it will be converted into a string using the rules described here. For example,
* `array('and', 'type=1', array('or', 'id=1', 'id=2'))` will generate `type=1 AND (id=1 OR id=2)`.
* The method will NOT do any quoting or escaping.
*
* - `or`: similar to the `and` operator except that the operands are concatenated using `OR`.
*
* - `between`: operand 1 should be the column name, and operand 2 and 3 should be the
* starting and ending values of the range that the column is in.
* For example, `array('between', 'id', 1, 10)` will generate `id BETWEEN 1 AND 10`.
*
* - `not between`: similar to `between` except the `BETWEEN` is replaced with `NOT BETWEEN`
* in the generated condition.
*
* - `in`: operand 1 should be a column or DB expression, and operand 2 be an array representing
* the range of the values that the column or DB expression should be in. For example,
* `array('in', 'id', array(1,2,3))` will generate `id IN (1,2,3)`.
* The method will properly quote the column name and escape values in the range.
*
* - `not in`: similar to the `in` operator except that `IN` is replaced with `NOT IN` in the generated condition.
*
* - `like`: operand 1 should be a column or DB expression, and operand 2 be a string or an array representing
* the values that the column or DB expression should be like.
* For example, `array('like', 'name', '%tester%')` will generate `name LIKE '%tester%'`.
* When the value range is given as an array, multiple `LIKE` predicates will be generated and concatenated
* using `AND`. For example, `array('like', 'name', array('%test%', '%sample%'))` will generate
* `name LIKE '%test%' AND name LIKE '%sample%'`.
* The method will properly quote the column name and escape values in the range.
*
* - `or like`: similar to the `like` operator except that `OR` is used to concatenate the `LIKE`
* predicates when operand 2 is an array.
*
* - `not like`: similar to the `like` operator except that `LIKE` is replaced with `NOT LIKE`
* in the generated condition.
*
* - `or not like`: similar to the `not like` operator except that `OR` is used to concatenate
* the `NOT LIKE` predicates.
*
* @param string|array $condition the conditions that should be put in the WHERE part.
* @param array $params the parameters (name=>value) to be bound to the query.
* For anonymous parameters, they can alternatively be specified as separate parameters to this method.
* For example, `where('type=? AND status=?', 100, 1)`.
* @return BaseQuery the query object itself
* @see andWhere()
* @see orWhere()
*/
public function where($condition, $params = array())
{
if (is_array($params)) {
$this->query->where($condition, $params);
} else {
call_user_func_array(array($this->query, __FUNCTION__), func_get_args());
}
return $this;
}
/**
* 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()]]
* on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return BaseQuery the query object itself
* @see where()
* @see orWhere()
*/
public function andWhere($condition, $params = array())
{
if (is_array($params)) {
$this->query->andWhere($condition, $params);
} else {
call_user_func_array(array($this->query, __FUNCTION__), func_get_args());
}
return $this;
}
/**
* 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()]]
* on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return BaseQuery the query object itself
* @see where()
* @see andWhere()
*/
public function orWhere($condition, $params = array())
{
if (is_array($params)) {
$this->query->orWhere($condition, $params);
} else {
call_user_func_array(array($this->query, __FUNCTION__), func_get_args());
}
return $this;
}
/**
* Appends an INNER JOIN part to the query.
* @param string $table the table to be joined.
* Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u').
* The method will automatically quote the table name unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
* @param string|array $condition the join condition that should appear in the ON part.
* Please refer to [[where()]] on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return BaseQuery the query object itself
*/
public function join($table, $condition, $params = array())
{
if (is_array($params)) {
$this->query->join($table, $condition, $params);
} else {
call_user_func_array(array($this->query, __FUNCTION__), func_get_args());
}
return $this;
}
/**
* Appends a LEFT OUTER JOIN part to the query.
* @param string $table the table to be joined.
* Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u').
* The method will automatically quote the table name unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
* @param string|array $condition the join condition that should appear in the ON 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 BaseQuery the query object itself
*/
public function leftJoin($table, $condition, $params = array())
{
if (is_array($params)) {
$this->query->leftJoin($table, $condition, $params);
} else {
call_user_func_array(array($this->query, __FUNCTION__), func_get_args());
}
return $this;
}
/**
* Appends a RIGHT OUTER JOIN part to the query.
* @param string $table the table to be joined.
* Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u').
* The method will automatically quote the table name unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
* @param string|array $condition the join condition that should appear in the ON 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 BaseQuery the query object itself
*/
public function rightJoin($table, $condition, $params = array())
{ {
$db = $this->getDbConnection(); if (is_array($params)) {
$this->sql = $this->getSql($db); $this->query->rightJoin($table, $condition, $params);
$command = $db->createCommand($this->sql); } else {
$command->bindValues($this->params); call_user_func_array(array($this->query, __FUNCTION__), func_get_args());
}
return $this;
}
/**
* Appends a CROSS JOIN part to the query.
* Note that not all DBMS support CROSS JOIN.
* @param string $table the table to be joined.
* Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u').
* The method will automatically quote the table name unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
* @return BaseQuery the query object itself
*/
public function crossJoin($table)
{
$this->query->crossJoin($table);
return $this;
}
/**
* Appends a NATURAL JOIN part to the query.
* Note that not all DBMS support NATURAL JOIN.
* @param string $table the table to be joined.
* Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u').
* The method will automatically quote the table name unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
* @return BaseQuery the query object itself
*/
public function naturalJoin($table)
{
$this->query->naturalJoin($table);
return $this;
}
/**
* Sets the GROUP BY part of the query.
* @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. array('id', 'name')).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return BaseQuery the query object itself
* @see addGroupBy()
*/
public function groupBy($columns)
{
$this->query->groupBy($columns);
return $this;
}
/**
* Adds additional group-by columns to the existing ones.
* @param string|array $columns additional columns to be grouped by.
* Columns can be specified in either a string (e.g. "id, name") or an array (e.g. array('id', 'name')).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return BaseQuery the query object itself
* @see groupBy()
*/
public function addGroupBy($columns)
{
$this->query->addGroupBy($columns);
return $this;
}
/**
* Sets the HAVING part of the query.
* @param string|array $condition the conditions to be put after HAVING.
* Please refer to [[where()]] on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return BaseQuery the query object itself
* @see andHaving()
* @see orHaving()
*/
public function having($condition, $params = array())
{
if (is_array($params)) {
$this->query->having($condition, $params);
} else {
call_user_func_array(array($this->query, __FUNCTION__), func_get_args());
}
return $this;
}
/**
* Adds an additional HAVING 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 HAVING condition. Please refer to [[where()]]
* on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return BaseQuery the query object itself
* @see having()
* @see orHaving()
*/
public function andHaving($condition, $params = array())
{
if (is_array($params)) {
$this->query->andHaving($condition, $params);
} else {
call_user_func_array(array($this->query, __FUNCTION__), func_get_args());
}
return $this;
}
/**
* Adds an additional HAVING 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 HAVING condition. Please refer to [[where()]]
* on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return BaseQuery the query object itself
* @see having()
* @see andHaving()
*/
public function orHaving($condition, $params = array())
{
if (is_array($params)) {
$this->query->orHaving($condition, $params);
} else {
call_user_func_array(array($this->query, __FUNCTION__), func_get_args());
}
return $this;
}
/**
* 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 (e.g. array('id ASC', 'name DESC')).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return BaseQuery the query object itself
* @see addOrderBy()
*/
public function orderBy($columns)
{
$this->query->orderBy($columns);
return $this;
}
/**
* Adds additional ORDER BY columns to 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 (e.g. array('id ASC', 'name DESC')).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return BaseQuery the query object itself
* @see orderBy()
*/
public function addOrderBy($columns)
{
$this->query->addOrderBy($columns);
return $this;
}
/**
* Sets the LIMIT part of the query.
* @param integer $limit the limit
* @return BaseQuery the query object itself
*/
public function limit($limit)
{
$this->query->limit($limit);
return $this;
}
/**
* Sets the OFFSET part of the query.
* @param integer $offset the offset
* @return BaseQuery the query object itself
*/
public function offset($offset)
{
$this->query->offset($offset);
return $this;
}
/**
* Appends a SQL statement using UNION operator.
* @param string|Query $sql the SQL statement to be appended using UNION
* @return BaseQuery the query object itself
*/
public function union($sql)
{
$this->query->union($sql);
return $this;
}
/**
* Sets the parameters to be bound to the query.
* @param array list of query parameter values indexed by parameter placeholders.
* For example, `array(':name'=>'Dan', ':age'=>31)`.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return BaseQuery the query object itself
* @see addParams()
*/
public function params($params)
{
$this->query->params($params);
return $this;
}
/**
* Adds additional parameters to be bound to the query.
* @param array list of query parameter values indexed by parameter placeholders.
* For example, `array(':name'=>'Dan', ':age'=>31)`.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return BaseQuery the query object itself
* @see params()
*/
public function addParams($params)
{
$this->query->addParams($params);
return $this;
}
protected function findRecords()
{
/*
* public $with;
*/
if ($this->query->from === null) {
$modelClass = $this->modelClass;
$this->query->from = $modelClass::tableName();
if ($this->tableAlias !== null) {
$this->query->from .= $this->tableAlias;
}
}
$command = $this->query->createCommand($this->getDbConnection());
$this->sql = $command->getSql();
$rows = $command->queryAll(); $rows = $command->queryAll();
if ($this->asArray) { if ($this->asArray) {
$records = $rows; if ($this->indexBy === null) {
return $rows;
}
$records = array();
foreach ($rows as $row) {
$records[$row[$this->indexBy]] = $row;
}
return $records;
} else { } else {
$records = array(); $records = array();
$class = $this->modelClass; $class = $this->modelClass;
if ($this->indexBy === null) {
foreach ($rows as $row) { foreach ($rows as $row) {
$records[] = $class::populateData($row); $records[] = $class::populateData($row);
} }
} else {
$attribute = $this->indexBy;
foreach ($rows as $row) {
$record = $class::populateData($row);
$records[$record->$attribute] = $record;
}
} }
return $records; return $records;
} }
}
protected function performCountQuery() protected function performCountQuery()
{ {
$this->select = 'COUNT(*)'; $this->query->select = 'COUNT(*)';
$class = $this->modelClass; $command = $this->query->createCommand($this->getDbConnection());
$command = $this->createCommand($class::getDbConnection());
$this->sql = $command->getSql(); $this->sql = $command->getSql();
$count = $command->queryScalar(); return $command->queryScalar();
return $count;
} }
} }
...@@ -17,5 +17,22 @@ namespace yii\db\ar; ...@@ -17,5 +17,22 @@ namespace yii\db\ar;
*/ */
class ActiveQueryBuilder extends \yii\base\Object class ActiveQueryBuilder extends \yii\base\Object
{ {
/**
* @var \yii\db\dao\QueryBuilder
*/
public $queryBuilder;
/**
* @var ActiveQuery
*/
public $query;
public function __construct($query)
{
$this->query = $query;
}
public function build()
{
}
} }
\ No newline at end of file
...@@ -53,23 +53,25 @@ abstract class ActiveRecord extends \yii\base\Model ...@@ -53,23 +53,25 @@ abstract class ActiveRecord extends \yii\base\Model
/** /**
* @static * @static
* @param string|array|ActiveQuery $q * @param string|array|Query $q
* @return ActiveQuery * @return ActiveQuery
* @throws \yii\db\Exception * @throws \yii\db\Exception
*/ */
public static function find($q = null) public static function find($q = null)
{ {
$query = $q instanceof ActiveQuery? $q : static::createQuery(); $query = static::createActiveQuery();
$query->modelClass = '\\' . get_called_class(); if ($q instanceof Query) {
$query->from = static::tableName(); $query->query = $q;
if (is_array($q)) { } elseif (is_array($q)) {
// query by attributes
$query->where($q); $query->where($q);
} elseif ($q !== null && $query !== $q) { } elseif ($q !== null) {
// query by primary key
$primaryKey = static::getMetaData()->table->primaryKey; $primaryKey = static::getMetaData()->table->primaryKey;
if (is_string($primaryKey)) { if (is_string($primaryKey)) {
$query->where(array($primaryKey => $q)); $query->where(array($primaryKey => $q));
} else { } else {
throw new Exception("Multiple column values are required to find by composite primary keys."); throw new Exception('Composite primary keys require multiple column values.');
} }
} }
return $query; return $query;
...@@ -77,21 +79,15 @@ abstract class ActiveRecord extends \yii\base\Model ...@@ -77,21 +79,15 @@ abstract class ActiveRecord extends \yii\base\Model
public static function findBySql($sql, $params = array()) public static function findBySql($sql, $params = array())
{ {
$query = static::createQuery();
if (!is_array($params)) { if (!is_array($params)) {
$params = func_get_args(); $params = func_get_args();
array_shift($params); array_shift($params);
} }
$query->setSql($sql); $query = static::createActiveQuery();
$query->modelClass = '\\' . get_called_class(); $query->sql = $sql;
return $query->params($params); return $query->params($params);
} }
public static function exists($condition, $params)
{
}
public static function updateAll() public static function updateAll()
{ {
...@@ -107,7 +103,7 @@ abstract class ActiveRecord extends \yii\base\Model ...@@ -107,7 +103,7 @@ abstract class ActiveRecord extends \yii\base\Model
} }
public static function createQuery() public static function createActiveQuery()
{ {
return new ActiveQuery('\\' . get_called_class()); return new ActiveQuery('\\' . get_called_class());
} }
......
<?php
/**
* BaseQuery class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008-2012 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\dao;
/**
* BaseQuery represents a SQL statement in a way that is independent of DBMS.
*
* BaseQuery not only can represent a SELECT statement, it can also represent INSERT, UPDATE, DELETE,
* and other commonly used DDL statements, such as CREATE TABLE, CREATE INDEX, etc.
*
* BaseQuery provides a set of methods to facilitate the specification of different clauses.
* These methods can be chained together. For example,
*
* ~~~
* $query = new Query;
* $query->select('id, name')
* ->from('tbl_user')
* ->limit(10);
* // get the actual SQL statement
* echo $query->getSql();
* // or execute the query
* $users = $query->createCommand()->queryAll();
* ~~~
*
* By calling [[getSql()]], we can obtain the actual SQL statement from a Query object.
* And by calling [[createCommand()]], we can get a [[Command]] instance which can be further
* used to perform/execute the DB query against a database.
*
* @property string $sql the SQL statement represented by this query object.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class BaseQuery extends \yii\base\Object
{
/**
* @var string|array the columns being selected. This refers to the SELECT clause in a SQL
* statement. It can be either a string (e.g. `'id, name'`) or an array (e.g. `array('id', 'name')`).
* If not set, if means all columns.
* @see select()
*/
public $select;
/**
* @var string additional option that should be appended to the 'SELECT' keyword. For example,
* in MySQL, the option 'SQL_CALC_FOUND_ROWS' can be used.
*/
public $selectOption;
/**
* @var boolean whether to select distinct rows of data only. If this is set true,
* the SELECT clause would be changed to SELECT DISTINCT.
*/
public $distinct;
/**
* @var string|array the table(s) to be selected from. This refers to the FROM clause in a SQL statement.
* It can be either a string (e.g. `'tbl_user, tbl_post'`) or an array (e.g. `array('tbl_user', 'tbl_post')`).
* @see from()
*/
public $from;
/**
* @var string|array query condition. This refers to the WHERE clause in a SQL statement.
* For example, `age > 31 AND team = 1`.
* @see where()
*/
public $where;
/**
* @var integer maximum number of records to be returned. If not set or less than 0, it means no limit.
*/
public $limit;
/**
* @var integer zero-based offset from where the records are to be returned. If not set or
* less than 0, it means starting from the beginning.
*/
public $offset;
/**
* @var string|array how to sort the query results. This refers to the ORDER BY clause in a SQL statement.
* It can be either a string (e.g. `'id ASC, name DESC'`) or an array (e.g. `array('id ASC', 'name DESC')`).
*/
public $orderBy;
/**
* @var string|array how to group the query results. This refers to the GROUP BY clause in a SQL statement.
* It can be either a string (e.g. `'company, department'`) or an array (e.g. `array('company', 'department')`).
*/
public $groupBy;
/**
* @var string|array how to join with other tables. This refers to the JOIN clause in a SQL statement.
* It can either a string (e.g. `'LEFT JOIN tbl_user ON tbl_user.id=author_id'`) or an array (e.g.
* `array('LEFT JOIN tbl_user ON tbl_user.id=author_id', 'LEFT JOIN tbl_team ON tbl_team.id=team_id')`).
* @see join()
*/
public $join;
/**
* @var string|array the condition to be applied in the GROUP BY clause.
* It can be either a string or an array. Please refer to [[where()]] on how to specify the condition.
*/
public $having;
/**
* @var array list of query parameter values indexed by parameter placeholders.
* For example, `array(':name'=>'Dan', ':age'=>31)`.
*/
public $params;
/**
* @var string|array the UNION clause(s) in a SQL statement. This can be either a string
* representing a single UNION clause or an array representing multiple UNION clauses.
* Each union clause can be a string or a `Query` object which refers to the SQL statement.
*/
public $union;
/**
* Sets the SELECT part of the query.
* @param mixed $columns the columns to be selected. Defaults to '*', meaning all columns.
* Columns can be specified in either a string (e.g. "id, name") or an array (e.g. array('id', 'name')).
* Columns can contain table prefixes (e.g. "tbl_user.id") and/or column aliases (e.g. "tbl_user.id AS user_id").
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @param string $option additional option that should be appended to the 'SELECT' keyword. For example,
* in MySQL, the option 'SQL_CALC_FOUND_ROWS' can be used.
* @return BaseQuery the query object itself
*/
public function select($columns = '*', $option = '')
{
$this->select = $columns;
$this->selectOption = $option;
return $this;
}
/**
* Sets the value indicating whether to SELECT DISTINCT or not.
* @param bool $value whether to SELECT DISTINCT or not.
* @return BaseQuery the query object itself
*/
public function distinct($value = true)
{
$this->distinct = $value;
return $this;
}
/**
* Sets the FROM part of the query.
* @param mixed $tables the table(s) to be selected from. This can be either a string (e.g. 'tbl_user')
* or an array (e.g. array('tbl_user', 'tbl_profile')) specifying one or several table names.
* Table names can contain schema prefixes (e.g. 'public.tbl_user') and/or table aliases (e.g. 'tbl_user u').
* The method will automatically quote the table names unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
* @return BaseQuery the query object itself
*/
public function from($tables)
{
$this->from = $tables;
return $this;
}
/**
* Sets the WHERE part of the query.
*
* The method requires a $condition parameter, and optionally a $params parameter
* specifying the values to be bound to the query.
*
* The $condition parameter should be either a string (e.g. 'id=1') or an array.
* If the latter, it must be in one of the following two formats:
*
* - hash format: `array('column1' => value1, 'column2' => value2, ...)`
* - operator format: `array(operator, operand1, operand2, ...)`
*
* A condition in hash format represents the following SQL expression in general:
* `column1=value1 AND column2=value2 AND ...`. In case when a value is an array,
* an `IN` expression will be generated. And if a value is null, `IS NULL` will be used
* in the generated expression. Below are some examples:
*
* - `array('type'=>1, 'status'=>2)` generates `(type=1) AND (status=2)`.
* - `array('id'=>array(1,2,3), 'status'=>2)` generates `(id IN (1,2,3)) AND (status=2)`.
* - `array('status'=>null) generates `status IS NULL`.
*
* A condition in operator format generates the SQL expression according to the specified operator, which
* can be one of the followings:
*
* - `and`: the operands should be concatenated together using `AND`. For example,
* `array('and', 'id=1', 'id=2')` will generate `id=1 AND id=2`. If an operand is an array,
* it will be converted into a string using the rules described here. For example,
* `array('and', 'type=1', array('or', 'id=1', 'id=2'))` will generate `type=1 AND (id=1 OR id=2)`.
* The method will NOT do any quoting or escaping.
*
* - `or`: similar to the `and` operator except that the operands are concatenated using `OR`.
*
* - `between`: operand 1 should be the column name, and operand 2 and 3 should be the
* starting and ending values of the range that the column is in.
* For example, `array('between', 'id', 1, 10)` will generate `id BETWEEN 1 AND 10`.
*
* - `not between`: similar to `between` except the `BETWEEN` is replaced with `NOT BETWEEN`
* in the generated condition.
*
* - `in`: operand 1 should be a column or DB expression, and operand 2 be an array representing
* the range of the values that the column or DB expression should be in. For example,
* `array('in', 'id', array(1,2,3))` will generate `id IN (1,2,3)`.
* The method will properly quote the column name and escape values in the range.
*
* - `not in`: similar to the `in` operator except that `IN` is replaced with `NOT IN` in the generated condition.
*
* - `like`: operand 1 should be a column or DB expression, and operand 2 be a string or an array representing
* the values that the column or DB expression should be like.
* For example, `array('like', 'name', '%tester%')` will generate `name LIKE '%tester%'`.
* When the value range is given as an array, multiple `LIKE` predicates will be generated and concatenated
* using `AND`. For example, `array('like', 'name', array('%test%', '%sample%'))` will generate
* `name LIKE '%test%' AND name LIKE '%sample%'`.
* The method will properly quote the column name and escape values in the range.
*
* - `or like`: similar to the `like` operator except that `OR` is used to concatenate the `LIKE`
* predicates when operand 2 is an array.
*
* - `not like`: similar to the `like` operator except that `LIKE` is replaced with `NOT LIKE`
* in the generated condition.
*
* - `or not like`: similar to the `not like` operator except that `OR` is used to concatenate
* the `NOT LIKE` predicates.
*
* @param string|array $condition the conditions that should be put in the WHERE part.
* @param array $params the parameters (name=>value) to be bound to the query.
* For anonymous parameters, they can alternatively be specified as separate parameters to this method.
* For example, `where('type=? AND status=?', 100, 1)`.
* @return BaseQuery the query object itself
* @see andWhere()
* @see orWhere()
*/
public function where($condition, $params = array())
{
$this->where = $condition;
if (!is_array($params)) {
$params = func_get_args();
unset($params[0]);
}
$this->addParams($params);
return $this;
}
/**
* 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()]]
* on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return BaseQuery the query object itself
* @see where()
* @see orWhere()
*/
public function andWhere($condition, $params = array())
{
if ($this->where === null) {
$this->where = $condition;
} else {
$this->where = array('and', $this->where, $condition);
}
if (!is_array($params)) {
$params = func_get_args();
unset($params[0]);
}
$this->addParams($params);
return $this;
}
/**
* 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()]]
* on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return BaseQuery the query object itself
* @see where()
* @see andWhere()
*/
public function orWhere($condition, $params = array())
{
if ($this->where === null) {
$this->where = $condition;
} else {
$this->where = array('or', $this->where, $condition);
}
if (!is_array($params)) {
$params = func_get_args();
unset($params[0]);
}
$this->addParams($params);
return $this;
}
/**
* Appends an INNER JOIN part to the query.
* @param string $table the table to be joined.
* Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u').
* The method will automatically quote the table name unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
* @param string|array $condition the join condition that should appear in the ON part.
* Please refer to [[where()]] on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return BaseQuery the query object itself
*/
public function join($table, $condition, $params = array())
{
$this->join[] = array('JOIN', $table, $condition);
if (!is_array($params)) {
$params = func_get_args();
array_shift($params);
unset($params[0]);
}
return $this->addParams($params);
}
/**
* Appends a LEFT OUTER JOIN part to the query.
* @param string $table the table to be joined.
* Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u').
* The method will automatically quote the table name unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
* @param string|array $condition the join condition that should appear in the ON 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 BaseQuery the query object itself
*/
public function leftJoin($table, $condition, $params = array())
{
$this->join[] = array('LEFT JOIN', $table, $condition);
if (!is_array($params)) {
$params = func_get_args();
array_shift($params);
unset($params[0]);
}
return $this->addParams($params);
}
/**
* Appends a RIGHT OUTER JOIN part to the query.
* @param string $table the table to be joined.
* Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u').
* The method will automatically quote the table name unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
* @param string|array $condition the join condition that should appear in the ON 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 BaseQuery the query object itself
*/
public function rightJoin($table, $condition, $params = array())
{
$this->join[] = array('RIGHT JOIN', $table, $condition);
if (!is_array($params)) {
$params = func_get_args();
array_shift($params);
unset($params[0]);
}
return $this->addParams($params);
}
/**
* Appends a CROSS JOIN part to the query.
* Note that not all DBMS support CROSS JOIN.
* @param string $table the table to be joined.
* Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u').
* The method will automatically quote the table name unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
* @return BaseQuery the query object itself
*/
public function crossJoin($table)
{
$this->join[] = array('CROSS JOIN', $table);
return $this;
}
/**
* Appends a NATURAL JOIN part to the query.
* Note that not all DBMS support NATURAL JOIN.
* @param string $table the table to be joined.
* Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u').
* The method will automatically quote the table name unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
* @return BaseQuery the query object itself
*/
public function naturalJoin($table)
{
$this->join[] = array('NATURAL JOIN', $table);
return $this;
}
/**
* Sets the GROUP BY part of the query.
* @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. array('id', 'name')).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return BaseQuery the query object itself
* @see addGroupBy()
*/
public function groupBy($columns)
{
$this->groupBy = $columns;
return $this;
}
/**
* Adds additional group-by columns to the existing ones.
* @param string|array $columns additional columns to be grouped by.
* Columns can be specified in either a string (e.g. "id, name") or an array (e.g. array('id', 'name')).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return BaseQuery the query object itself
* @see groupBy()
*/
public function addGroupBy($columns)
{
if (empty($this->groupBy)) {
$this->groupBy = $columns;
} else {
if (!is_array($this->groupBy)) {
$this->groupBy = preg_split('/\s*,\s*/', trim($this->groupBy), -1, PREG_SPLIT_NO_EMPTY);
}
if (!is_array($columns)) {
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
}
$this->groupBy = array_merge($this->groupBy, $columns);
}
return $this;
}
/**
* Sets the HAVING part of the query.
* @param string|array $condition the conditions to be put after HAVING.
* Please refer to [[where()]] on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return BaseQuery the query object itself
* @see andHaving()
* @see orHaving()
*/
public function having($condition, $params = array())
{
$this->having = $condition;
if (!is_array($params)) {
$params = func_get_args();
unset($params[0]);
}
$this->addParams($params);
return $this;
}
/**
* Adds an additional HAVING 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 HAVING condition. Please refer to [[where()]]
* on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return BaseQuery the query object itself
* @see having()
* @see orHaving()
*/
public function andHaving($condition, $params = array())
{
if ($this->having === null) {
$this->having = $condition;
} else {
$this->having = array('and', $this->having, $condition);
}
if (!is_array($params)) {
$params = func_get_args();
unset($params[0]);
}
$this->addParams($params);
return $this;
}
/**
* Adds an additional HAVING 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 HAVING condition. Please refer to [[where()]]
* on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return BaseQuery the query object itself
* @see having()
* @see andHaving()
*/
public function orHaving($condition, $params = array())
{
if ($this->having === null) {
$this->having = $condition;
} else {
$this->having = array('or', $this->having, $condition);
}
if (!is_array($params)) {
$params = func_get_args();
unset($params[0]);
}
$this->addParams($params);
return $this;
}
/**
* 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 (e.g. array('id ASC', 'name DESC')).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return BaseQuery the query object itself
* @see addOrderBy()
*/
public function orderBy($columns)
{
$this->orderBy = $columns;
return $this;
}
/**
* Adds additional ORDER BY columns to 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 (e.g. array('id ASC', 'name DESC')).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return BaseQuery the query object itself
* @see orderBy()
*/
public function addOrderBy($columns)
{
if (empty($this->orderBy)) {
$this->orderBy = $columns;
} else {
if (!is_array($this->orderBy)) {
$this->orderBy = preg_split('/\s*,\s*/', trim($this->orderBy), -1, PREG_SPLIT_NO_EMPTY);
}
if (!is_array($columns)) {
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
}
$this->orderBy = array_merge($this->orderBy, $columns);
}
return $this;
}
/**
* Sets the LIMIT part of the query.
* @param integer $limit the limit
* @return BaseQuery the query object itself
*/
public function limit($limit)
{
$this->limit = $limit;
return $this;
}
/**
* Sets the OFFSET part of the query.
* @param integer $offset the offset
* @return BaseQuery the query object itself
*/
public function offset($offset)
{
$this->offset = $offset;
return $this;
}
/**
* Appends a SQL statement using UNION operator.
* @param string $sql the SQL statement to be appended using UNION
* @return BaseQuery the query object itself
*/
public function union($sql)
{
$this->union[] = $sql;
return $this;
}
/**
* Sets the parameters to be bound to the query.
* @param array list of query parameter values indexed by parameter placeholders.
* For example, `array(':name'=>'Dan', ':age'=>31)`.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return BaseQuery the query object itself
* @see addParams()
*/
public function params($params)
{
$this->params = $params;
return $this;
}
/**
* Adds additional parameters to be bound to the query.
* @param array list of query parameter values indexed by parameter placeholders.
* For example, `array(':name'=>'Dan', ':age'=>31)`.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return BaseQuery the query object itself
* @see params()
*/
public function addParams($params)
{
if ($this->params === null) {
$this->params = $params;
} else {
foreach ($params as $name => $value) {
if (is_integer($name)) {
$this->params[] = $value;
} else {
$this->params[$name] = $value;
}
}
}
return $this;
}
/**
* Merges this query with another one.
*
* The merging is done according to the following rules:
*
* - [[select]]: the union of both queries' [[select]] property values.
* - [[selectOption]], [[distinct]], [[limit]], [[offset]]: the new query
* takes precedence over this query.
* - [[where]], [[having]]: the new query's corresponding property value
* will be 'AND' together with the existing one.
* - [[params]], [[orderBy]], [[groupBy]], [[join]], [[union]]: the new query's
* corresponding property value will be appended to the existing one.
*
* In general, the merging makes the resulting query more restrictive and specific.
* @param BaseQuery $query the new query to be merged with this query.
* @return BaseQuery the query object itself
*/
public function mergeWith($query)
{
if ($this->select !== $query->select) {
if (empty($this->select)) {
$this->select = $query->select;
} elseif (!empty($query->select)) {
$select1 = is_string($this->select) ? preg_split('/\s*,\s*/', trim($this->select), -1, PREG_SPLIT_NO_EMPTY) : $this->select;
$select2 = is_string($query->select) ? preg_split('/\s*,\s*/', trim($query->select), -1, PREG_SPLIT_NO_EMPTY) : $query->select;
$this->select = array_merge($select1, array_diff($select2, $select1));
}
}
if ($query->selectOption !== null) {
$this->selectOption = $query->selectOption;
}
if ($query->distinct !== null) {
$this->distinct = $query->distinct;
}
if ($query->limit !== null) {
$this->limit = $query->limit;
}
if ($query->offset !== null) {
$this->offset = $query->offset;
}
if ($query->where !== null) {
$this->andWhere($query->where);
}
if ($query->having !== null) {
$this->andHaving($query->having);
}
if ($query->params !== null) {
$this->addParams($query->params);
}
if ($query->orderBy !== null) {
$this->addOrderBy($query->orderBy);
}
if ($query->groupBy !== null) {
$this->addGroupBy($query->groupBy);
}
if ($query->join !== null) {
if (empty($this->join)) {
$this->join = $query->join;
} else {
if (!is_array($this->join)) {
$this->join = array($this->join);
}
if (is_array($query->join)) {
$this->join = array_merge($this->join, $query->join);
} else {
$this->join[] = $query->join;
}
}
}
if ($query->union !== null) {
if (empty($this->union)) {
$this->union = $query->union;
} else {
if (!is_array($this->union)) {
$this->union = array($this->union);
}
if (is_array($query->union)) {
$this->union = array_merge($this->union, $query->union);
} else {
$this->union[] = $query->union;
}
}
}
return $this;
}
/**
* Resets the query object to its original state.
* @return BaseQuery the query object itself
*/
public function reset()
{
foreach (get_object_vars($this) as $name => $value) {
$this->$name = null;
}
return $this;
}
}
...@@ -39,7 +39,7 @@ namespace yii\db\dao; ...@@ -39,7 +39,7 @@ namespace yii\db\dao;
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class Query extends BaseQuery class Query extends \yii\base\Object
{ {
/** /**
* @var array the operation that this query represents. This refers to the method call as well as * @var array the operation that this query represents. This refers to the method call as well as
...@@ -48,7 +48,77 @@ class Query extends BaseQuery ...@@ -48,7 +48,77 @@ class Query extends BaseQuery
* If this property is not set, it means this query represents a SELECT statement. * If this property is not set, it means this query represents a SELECT statement.
*/ */
public $operation; public $operation;
/**
* @var string|array the columns being selected. This refers to the SELECT clause in a SQL
* statement. It can be either a string (e.g. `'id, name'`) or an array (e.g. `array('id', 'name')`).
* If not set, if means all columns.
* @see select()
*/
public $select;
/**
* @var string additional option that should be appended to the 'SELECT' keyword. For example,
* in MySQL, the option 'SQL_CALC_FOUND_ROWS' can be used.
*/
public $selectOption;
/**
* @var boolean whether to select distinct rows of data only. If this is set true,
* the SELECT clause would be changed to SELECT DISTINCT.
*/
public $distinct;
/**
* @var string|array the table(s) to be selected from. This refers to the FROM clause in a SQL statement.
* It can be either a string (e.g. `'tbl_user, tbl_post'`) or an array (e.g. `array('tbl_user', 'tbl_post')`).
* @see from()
*/
public $from;
/**
* @var string|array query condition. This refers to the WHERE clause in a SQL statement.
* For example, `age > 31 AND team = 1`.
* @see where()
*/
public $where;
/**
* @var integer maximum number of records to be returned. If not set or less than 0, it means no limit.
*/
public $limit;
/**
* @var integer zero-based offset from where the records are to be returned. If not set or
* less than 0, it means starting from the beginning.
*/
public $offset;
/**
* @var string|array how to sort the query results. This refers to the ORDER BY clause in a SQL statement.
* It can be either a string (e.g. `'id ASC, name DESC'`) or an array (e.g. `array('id ASC', 'name DESC')`).
*/
public $orderBy;
/**
* @var string|array how to group the query results. This refers to the GROUP BY clause in a SQL statement.
* It can be either a string (e.g. `'company, department'`) or an array (e.g. `array('company', 'department')`).
*/
public $groupBy;
/**
* @var string|array how to join with other tables. This refers to the JOIN clause in a SQL statement.
* It can either a string (e.g. `'LEFT JOIN tbl_user ON tbl_user.id=author_id'`) or an array (e.g.
* `array('LEFT JOIN tbl_user ON tbl_user.id=author_id', 'LEFT JOIN tbl_team ON tbl_team.id=team_id')`).
* @see join()
*/
public $join;
/**
* @var string|array the condition to be applied in the GROUP BY clause.
* It can be either a string or an array. Please refer to [[where()]] on how to specify the condition.
*/
public $having;
/**
* @var array list of query parameter values indexed by parameter placeholders.
* For example, `array(':name'=>'Dan', ':age'=>31)`.
*/
public $params;
/**
* @var string|Query[] the UNION clause(s) in a SQL statement. This can be either a string
* representing a single UNION clause or an array representing multiple UNION clauses.
* Each union clause can be a string or a `Query` object which refers to the SQL statement.
*/
public $union;
/** /**
* Generates and returns the SQL statement according to this query. * Generates and returns the SQL statement according to this query.
...@@ -304,4 +374,615 @@ class Query extends BaseQuery ...@@ -304,4 +374,615 @@ class Query extends BaseQuery
$this->operation = array(__FUNCTION__, $name, $table); $this->operation = array(__FUNCTION__, $name, $table);
return $this; return $this;
} }
/**
* Sets the SELECT part of the query.
* @param mixed $columns the columns to be selected. Defaults to '*', meaning all columns.
* Columns can be specified in either a string (e.g. "id, name") or an array (e.g. array('id', 'name')).
* Columns can contain table prefixes (e.g. "tbl_user.id") and/or column aliases (e.g. "tbl_user.id AS user_id").
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @param string $option additional option that should be appended to the 'SELECT' keyword. For example,
* in MySQL, the option 'SQL_CALC_FOUND_ROWS' can be used.
* @return Query the query object itself
*/
public function select($columns = '*', $option = '')
{
$this->select = $columns;
$this->selectOption = $option;
return $this;
}
/**
* Sets the value indicating whether to SELECT DISTINCT or not.
* @param bool $value whether to SELECT DISTINCT or not.
* @return Query the query object itself
*/
public function distinct($value = true)
{
$this->distinct = $value;
return $this;
}
/**
* Sets the FROM part of the query.
* @param mixed $tables the table(s) to be selected from. This can be either a string (e.g. 'tbl_user')
* or an array (e.g. array('tbl_user', 'tbl_profile')) specifying one or several table names.
* Table names can contain schema prefixes (e.g. 'public.tbl_user') and/or table aliases (e.g. 'tbl_user u').
* The method will automatically quote the table names unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
* @return Query the query object itself
*/
public function from($tables)
{
$this->from = $tables;
return $this;
}
/**
* Sets the WHERE part of the query.
*
* The method requires a $condition parameter, and optionally a $params parameter
* specifying the values to be bound to the query.
*
* The $condition parameter should be either a string (e.g. 'id=1') or an array.
* If the latter, it must be in one of the following two formats:
*
* - hash format: `array('column1' => value1, 'column2' => value2, ...)`
* - operator format: `array(operator, operand1, operand2, ...)`
*
* A condition in hash format represents the following SQL expression in general:
* `column1=value1 AND column2=value2 AND ...`. In case when a value is an array,
* an `IN` expression will be generated. And if a value is null, `IS NULL` will be used
* in the generated expression. Below are some examples:
*
* - `array('type'=>1, 'status'=>2)` generates `(type=1) AND (status=2)`.
* - `array('id'=>array(1,2,3), 'status'=>2)` generates `(id IN (1,2,3)) AND (status=2)`.
* - `array('status'=>null) generates `status IS NULL`.
*
* A condition in operator format generates the SQL expression according to the specified operator, which
* can be one of the followings:
*
* - `and`: the operands should be concatenated together using `AND`. For example,
* `array('and', 'id=1', 'id=2')` will generate `id=1 AND id=2`. If an operand is an array,
* it will be converted into a string using the rules described here. For example,
* `array('and', 'type=1', array('or', 'id=1', 'id=2'))` will generate `type=1 AND (id=1 OR id=2)`.
* The method will NOT do any quoting or escaping.
*
* - `or`: similar to the `and` operator except that the operands are concatenated using `OR`.
*
* - `between`: operand 1 should be the column name, and operand 2 and 3 should be the
* starting and ending values of the range that the column is in.
* For example, `array('between', 'id', 1, 10)` will generate `id BETWEEN 1 AND 10`.
*
* - `not between`: similar to `between` except the `BETWEEN` is replaced with `NOT BETWEEN`
* in the generated condition.
*
* - `in`: operand 1 should be a column or DB expression, and operand 2 be an array representing
* the range of the values that the column or DB expression should be in. For example,
* `array('in', 'id', array(1,2,3))` will generate `id IN (1,2,3)`.
* The method will properly quote the column name and escape values in the range.
*
* - `not in`: similar to the `in` operator except that `IN` is replaced with `NOT IN` in the generated condition.
*
* - `like`: operand 1 should be a column or DB expression, and operand 2 be a string or an array representing
* the values that the column or DB expression should be like.
* For example, `array('like', 'name', '%tester%')` will generate `name LIKE '%tester%'`.
* When the value range is given as an array, multiple `LIKE` predicates will be generated and concatenated
* using `AND`. For example, `array('like', 'name', array('%test%', '%sample%'))` will generate
* `name LIKE '%test%' AND name LIKE '%sample%'`.
* The method will properly quote the column name and escape values in the range.
*
* - `or like`: similar to the `like` operator except that `OR` is used to concatenate the `LIKE`
* predicates when operand 2 is an array.
*
* - `not like`: similar to the `like` operator except that `LIKE` is replaced with `NOT LIKE`
* in the generated condition.
*
* - `or not like`: similar to the `not like` operator except that `OR` is used to concatenate
* the `NOT LIKE` predicates.
*
* @param string|array $condition the conditions that should be put in the WHERE part.
* @param array $params the parameters (name=>value) to be bound to the query.
* For anonymous parameters, they can alternatively be specified as separate parameters to this method.
* For example, `where('type=? AND status=?', 100, 1)`.
* @return Query the query object itself
* @see andWhere()
* @see orWhere()
*/
public function where($condition, $params = array())
{
$this->where = $condition;
if (!is_array($params)) {
$params = func_get_args();
unset($params[0]);
}
$this->addParams($params);
return $this;
}
/**
* 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()]]
* on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return Query the query object itself
* @see where()
* @see orWhere()
*/
public function andWhere($condition, $params = array())
{
if ($this->where === null) {
$this->where = $condition;
} else {
$this->where = array('and', $this->where, $condition);
}
if (!is_array($params)) {
$params = func_get_args();
unset($params[0]);
}
$this->addParams($params);
return $this;
}
/**
* 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()]]
* on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return Query the query object itself
* @see where()
* @see andWhere()
*/
public function orWhere($condition, $params = array())
{
if ($this->where === null) {
$this->where = $condition;
} else {
$this->where = array('or', $this->where, $condition);
}
if (!is_array($params)) {
$params = func_get_args();
unset($params[0]);
}
$this->addParams($params);
return $this;
}
/**
* Appends an INNER JOIN part to the query.
* @param string $table the table to be joined.
* Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u').
* The method will automatically quote the table name unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
* @param string|array $condition the join condition that should appear in the ON part.
* Please refer to [[where()]] on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return Query the query object itself
*/
public function join($table, $condition, $params = array())
{
$this->join[] = array('JOIN', $table, $condition);
if (!is_array($params)) {
$params = func_get_args();
array_shift($params);
unset($params[0]);
}
return $this->addParams($params);
}
/**
* Appends a LEFT OUTER JOIN part to the query.
* @param string $table the table to be joined.
* Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u').
* The method will automatically quote the table name unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
* @param string|array $condition the join condition that should appear in the ON 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 Query the query object itself
*/
public function leftJoin($table, $condition, $params = array())
{
$this->join[] = array('LEFT JOIN', $table, $condition);
if (!is_array($params)) {
$params = func_get_args();
array_shift($params);
unset($params[0]);
}
return $this->addParams($params);
}
/**
* Appends a RIGHT OUTER JOIN part to the query.
* @param string $table the table to be joined.
* Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u').
* The method will automatically quote the table name unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
* @param string|array $condition the join condition that should appear in the ON 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 Query the query object itself
*/
public function rightJoin($table, $condition, $params = array())
{
$this->join[] = array('RIGHT JOIN', $table, $condition);
if (!is_array($params)) {
$params = func_get_args();
array_shift($params);
unset($params[0]);
}
return $this->addParams($params);
}
/**
* Appends a CROSS JOIN part to the query.
* Note that not all DBMS support CROSS JOIN.
* @param string $table the table to be joined.
* Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u').
* The method will automatically quote the table name unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
* @return Query the query object itself
*/
public function crossJoin($table)
{
$this->join[] = array('CROSS JOIN', $table);
return $this;
}
/**
* Appends a NATURAL JOIN part to the query.
* Note that not all DBMS support NATURAL JOIN.
* @param string $table the table to be joined.
* Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u').
* The method will automatically quote the table name unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
* @return Query the query object itself
*/
public function naturalJoin($table)
{
$this->join[] = array('NATURAL JOIN', $table);
return $this;
}
/**
* Sets the GROUP BY part of the query.
* @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. array('id', 'name')).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return Query the query object itself
* @see addGroupBy()
*/
public function groupBy($columns)
{
$this->groupBy = $columns;
return $this;
}
/**
* Adds additional group-by columns to the existing ones.
* @param string|array $columns additional columns to be grouped by.
* Columns can be specified in either a string (e.g. "id, name") or an array (e.g. array('id', 'name')).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return Query the query object itself
* @see groupBy()
*/
public function addGroupBy($columns)
{
if (empty($this->groupBy)) {
$this->groupBy = $columns;
} else {
if (!is_array($this->groupBy)) {
$this->groupBy = preg_split('/\s*,\s*/', trim($this->groupBy), -1, PREG_SPLIT_NO_EMPTY);
}
if (!is_array($columns)) {
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
}
$this->groupBy = array_merge($this->groupBy, $columns);
}
return $this;
}
/**
* Sets the HAVING part of the query.
* @param string|array $condition the conditions to be put after HAVING.
* Please refer to [[where()]] on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return Query the query object itself
* @see andHaving()
* @see orHaving()
*/
public function having($condition, $params = array())
{
$this->having = $condition;
if (!is_array($params)) {
$params = func_get_args();
unset($params[0]);
}
$this->addParams($params);
return $this;
}
/**
* Adds an additional HAVING 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 HAVING condition. Please refer to [[where()]]
* on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return Query the query object itself
* @see having()
* @see orHaving()
*/
public function andHaving($condition, $params = array())
{
if ($this->having === null) {
$this->having = $condition;
} else {
$this->having = array('and', $this->having, $condition);
}
if (!is_array($params)) {
$params = func_get_args();
unset($params[0]);
}
$this->addParams($params);
return $this;
}
/**
* Adds an additional HAVING 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 HAVING condition. Please refer to [[where()]]
* on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return Query the query object itself
* @see having()
* @see andHaving()
*/
public function orHaving($condition, $params = array())
{
if ($this->having === null) {
$this->having = $condition;
} else {
$this->having = array('or', $this->having, $condition);
}
if (!is_array($params)) {
$params = func_get_args();
unset($params[0]);
}
$this->addParams($params);
return $this;
}
/**
* 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 (e.g. array('id ASC', 'name DESC')).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return Query the query object itself
* @see addOrderBy()
*/
public function orderBy($columns)
{
$this->orderBy = $columns;
return $this;
}
/**
* Adds additional ORDER BY columns to 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 (e.g. array('id ASC', 'name DESC')).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return Query the query object itself
* @see orderBy()
*/
public function addOrderBy($columns)
{
if (empty($this->orderBy)) {
$this->orderBy = $columns;
} else {
if (!is_array($this->orderBy)) {
$this->orderBy = preg_split('/\s*,\s*/', trim($this->orderBy), -1, PREG_SPLIT_NO_EMPTY);
}
if (!is_array($columns)) {
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
}
$this->orderBy = array_merge($this->orderBy, $columns);
}
return $this;
}
/**
* Sets the LIMIT part of the query.
* @param integer $limit the limit
* @return Query the query object itself
*/
public function limit($limit)
{
$this->limit = $limit;
return $this;
}
/**
* Sets the OFFSET part of the query.
* @param integer $offset the offset
* @return Query the query object itself
*/
public function offset($offset)
{
$this->offset = $offset;
return $this;
}
/**
* Appends a SQL statement using UNION operator.
* @param string|Query $sql the SQL statement to be appended using UNION
* @return Query the query object itself
*/
public function union($sql)
{
$this->union[] = $sql;
return $this;
}
/**
* Sets the parameters to be bound to the query.
* @param array list of query parameter values indexed by parameter placeholders.
* For example, `array(':name'=>'Dan', ':age'=>31)`.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return Query the query object itself
* @see addParams()
*/
public function params($params)
{
$this->params = $params;
return $this;
}
/**
* Adds additional parameters to be bound to the query.
* @param array list of query parameter values indexed by parameter placeholders.
* For example, `array(':name'=>'Dan', ':age'=>31)`.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return Query the query object itself
* @see params()
*/
public function addParams($params)
{
if ($params !== array()) {
if ($this->params === null) {
$this->params = $params;
} else {
foreach ($params as $name => $value) {
if (is_integer($name)) {
$this->params[] = $value;
} else {
$this->params[$name] = $value;
}
}
}
}
return $this;
}
/**
* Merges this query with another one.
*
* The merging is done according to the following rules:
*
* - [[select]]: the union of both queries' [[select]] property values.
* - [[selectOption]], [[distinct]], [[limit]], [[offset]]: the new query
* takes precedence over this query.
* - [[where]], [[having]]: the new query's corresponding property value
* will be 'AND' together with the existing one.
* - [[params]], [[orderBy]], [[groupBy]], [[join]], [[union]]: the new query's
* corresponding property value will be appended to the existing one.
*
* In general, the merging makes the resulting query more restrictive and specific.
* @param Query $query the new query to be merged with this query.
* @return Query the query object itself
*/
public function mergeWith($query)
{
if ($this->select !== $query->select) {
if (empty($this->select)) {
$this->select = $query->select;
} elseif (!empty($query->select)) {
$select1 = is_string($this->select) ? preg_split('/\s*,\s*/', trim($this->select), -1, PREG_SPLIT_NO_EMPTY) : $this->select;
$select2 = is_string($query->select) ? preg_split('/\s*,\s*/', trim($query->select), -1, PREG_SPLIT_NO_EMPTY) : $query->select;
$this->select = array_merge($select1, array_diff($select2, $select1));
}
}
if ($query->selectOption !== null) {
$this->selectOption = $query->selectOption;
}
if ($query->distinct !== null) {
$this->distinct = $query->distinct;
}
if ($query->limit !== null) {
$this->limit = $query->limit;
}
if ($query->offset !== null) {
$this->offset = $query->offset;
}
if ($query->where !== null) {
$this->andWhere($query->where);
}
if ($query->having !== null) {
$this->andHaving($query->having);
}
if ($query->params !== null) {
$this->addParams($query->params);
}
if ($query->orderBy !== null) {
$this->addOrderBy($query->orderBy);
}
if ($query->groupBy !== null) {
$this->addGroupBy($query->groupBy);
}
if ($query->join !== null) {
if (empty($this->join)) {
$this->join = $query->join;
} else {
if (!is_array($this->join)) {
$this->join = array($this->join);
}
if (is_array($query->join)) {
$this->join = array_merge($this->join, $query->join);
} else {
$this->join[] = $query->join;
}
}
}
if ($query->union !== null) {
if (empty($this->union)) {
$this->union = $query->union;
} else {
if (!is_array($this->union)) {
$this->union = array($this->union);
}
if (is_array($query->union)) {
$this->union = array_merge($this->union, $query->union);
} else {
$this->union[] = $query->union;
}
}
}
return $this;
}
/**
* Resets the query object to its original state.
* @return Query the query object itself
*/
public function reset()
{
foreach (get_object_vars($this) as $name => $value) {
$this->$name = null;
}
return $this;
}
} }
...@@ -24,33 +24,28 @@ use yii\db\Exception; ...@@ -24,33 +24,28 @@ use yii\db\Exception;
class QueryBuilder extends \yii\base\Object class QueryBuilder extends \yii\base\Object
{ {
/** /**
* @var array the abstract column types mapped to physical column types.
* This is mainly used to support creating/modifying tables using DB-independent data type specifications.
* Child classes should override this property to declare supported type mappings.
*/
public $typeMap = array();
/**
* @var Connection the database connection. * @var Connection the database connection.
*/ */
public $connection; public $connection;
/** /**
* @var Driver the database driver used for this query builder.
*/
public $driver;
/**
* @var string the separator between different fragments of a SQL statement. * @var string the separator between different fragments of a SQL statement.
* Defaults to an empty space. This is mainly used by [[build()]] when generating a SQL statement. * Defaults to an empty space. This is mainly used by [[build()]] when generating a SQL statement.
*/ */
public $separator = " "; public $separator = " ";
/** /**
* @var Query the Query object that is currently processed by the query builder to generate a SQL statement.
* This property will be set null upon completion of [[build()]].
*/
public $query;
/**
* @var boolean whether to automatically quote table and column names when generating SQL statements. * @var boolean whether to automatically quote table and column names when generating SQL statements.
*/ */
public $autoQuote = true; public $autoQuote = true;
/**
* @var array the abstract column types mapped to physical column types.
* This is mainly used to support creating/modifying tables using DB-independent data type specifications.
* Child classes should override this property to declare supported type mappings.
*/
public $typeMap = array();
/**
* @var Query the Query object that is currently processed by the query builder to generate a SQL statement.
*/
public $query;
/** /**
* Constructor. * Constructor.
...@@ -59,7 +54,6 @@ class QueryBuilder extends \yii\base\Object ...@@ -59,7 +54,6 @@ class QueryBuilder extends \yii\base\Object
public function __construct($connection) public function __construct($connection)
{ {
$this->connection = $connection; $this->connection = $connection;
$this->driver = $connection->getDriver();
} }
/** /**
...@@ -74,8 +68,9 @@ class QueryBuilder extends \yii\base\Object ...@@ -74,8 +68,9 @@ class QueryBuilder extends \yii\base\Object
$this->query = $query; $this->query = $query;
if ($query->operation !== null) { if ($query->operation !== null) {
// non-SELECT query // non-SELECT query
$method = array_shift($query->operation); $params = $query->operation;
$sql = call_user_func_array(array($this, $method), $query->operation); $method = array_shift($params);
return call_user_func_array(array($this, $method), $params);
} else { } else {
// SELECT query // SELECT query
$clauses = array( $clauses = array(
...@@ -89,10 +84,8 @@ class QueryBuilder extends \yii\base\Object ...@@ -89,10 +84,8 @@ class QueryBuilder extends \yii\base\Object
$this->buildOrderBy(), $this->buildOrderBy(),
$this->buildLimit(), $this->buildLimit(),
); );
$sql = implode($this->separator, array_filter($clauses)); return implode($this->separator, array_filter($clauses));
} }
$this->query = null;
return $sql;
} }
/** /**
...@@ -477,7 +470,7 @@ class QueryBuilder extends \yii\base\Object ...@@ -477,7 +470,7 @@ class QueryBuilder extends \yii\base\Object
/** /**
* Parses the condition specification and generates the corresponding SQL expression. * Parses the condition specification and generates the corresponding SQL expression.
* @param string|array $condition the condition specification. Please refer to [[Query::where()]] * @param string|array $condition the condition specification. Please refer to [[BaseQuery::where()]]
* on how to specify a condition. * on how to specify a condition.
* @return string the generated SQL expression * @return string the generated SQL expression
* @throws \yii\db\Exception if the condition is in bad format * @throws \yii\db\Exception if the condition is in bad format
...@@ -651,6 +644,7 @@ class QueryBuilder extends \yii\base\Object ...@@ -651,6 +644,7 @@ class QueryBuilder extends \yii\base\Object
} }
if ($this->autoQuote) { if ($this->autoQuote) {
$driver = $this->connection->driver;
if (!is_array($columns)) { if (!is_array($columns)) {
if (strpos($columns, '(') !== false) { if (strpos($columns, '(') !== false) {
return $select . ' ' . $columns; return $select . ' ' . $columns;
...@@ -663,9 +657,9 @@ class QueryBuilder extends \yii\base\Object ...@@ -663,9 +657,9 @@ class QueryBuilder extends \yii\base\Object
$columns[$i] = (string)$column; $columns[$i] = (string)$column;
} elseif (strpos($column, '(') === false) { } elseif (strpos($column, '(') === false) {
if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)([\w\-\.])$/', $column, $matches)) { if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)([\w\-\.])$/', $column, $matches)) {
$columns[$i] = $this->driver->quoteColumnName($matches[1]) . ' AS ' . $this->driver->quoteSimpleColumnName($matches[2]); $columns[$i] = $driver->quoteColumnName($matches[1]) . ' AS ' . $driver->quoteSimpleColumnName($matches[2]);
} else { } else {
$columns[$i] = $this->driver->quoteColumnName($column); $columns[$i] = $driver->quoteColumnName($column);
} }
} }
} }
...@@ -690,6 +684,7 @@ class QueryBuilder extends \yii\base\Object ...@@ -690,6 +684,7 @@ class QueryBuilder extends \yii\base\Object
$tables = $this->query->from; $tables = $this->query->from;
if ($this->autoQuote) { if ($this->autoQuote) {
$driver = $this->connection->driver;
if (!is_array($tables)) { if (!is_array($tables)) {
if (strpos($tables, '(') !== false) { if (strpos($tables, '(') !== false) {
return 'FROM ' . $tables; return 'FROM ' . $tables;
...@@ -700,9 +695,9 @@ class QueryBuilder extends \yii\base\Object ...@@ -700,9 +695,9 @@ class QueryBuilder extends \yii\base\Object
foreach ($tables as $i => $table) { foreach ($tables as $i => $table) {
if (strpos($table, '(') === false) { if (strpos($table, '(') === false) {
if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/i', $table, $matches)) { // with alias if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/i', $table, $matches)) { // with alias
$tables[$i] = $this->driver->quoteTableName($matches[1]) . ' ' . $this->driver->quoteTableName($matches[2]); $tables[$i] = $driver->quoteTableName($matches[1]) . ' ' . $driver->quoteTableName($matches[2]);
} else { } else {
$tables[$i] = $this->driver->quoteTableName($table); $tables[$i] = $driver->quoteTableName($table);
} }
} }
} }
...@@ -733,10 +728,11 @@ class QueryBuilder extends \yii\base\Object ...@@ -733,10 +728,11 @@ class QueryBuilder extends \yii\base\Object
if (isset($join[0], $join[1])) { if (isset($join[0], $join[1])) {
$table = $join[1]; $table = $join[1];
if ($this->autoQuote && strpos($table, '(') === false) { if ($this->autoQuote && strpos($table, '(') === false) {
$driver = $this->connection->driver;
if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/', $table, $matches)) { // with alias if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/', $table, $matches)) { // with alias
$table = $this->driver->quoteTableName($matches[1]) . ' ' . $this->driver->quoteTableName($matches[2]); $table = $driver->quoteTableName($matches[1]) . ' ' . $driver->quoteTableName($matches[2]);
} else { } else {
$table = $this->driver->quoteTableName($table); $table = $driver->quoteTableName($table);
} }
} }
$joins[$i] = strtoupper($join[0]) . ' ' . $table; $joins[$i] = strtoupper($join[0]) . ' ' . $table;
...@@ -792,6 +788,7 @@ class QueryBuilder extends \yii\base\Object ...@@ -792,6 +788,7 @@ class QueryBuilder extends \yii\base\Object
} }
$columns = $this->query->orderBy; $columns = $this->query->orderBy;
if ($this->autoQuote) { if ($this->autoQuote) {
$driver = $this->connection->driver;
if (!is_array($columns)) { if (!is_array($columns)) {
if (strpos($columns, '(') !== false) { if (strpos($columns, '(') !== false) {
return 'ORDER BY ' . $columns; return 'ORDER BY ' . $columns;
...@@ -804,9 +801,9 @@ class QueryBuilder extends \yii\base\Object ...@@ -804,9 +801,9 @@ class QueryBuilder extends \yii\base\Object
$columns[$i] = (string)$column; $columns[$i] = (string)$column;
} elseif (strpos($column, '(') === false) { } elseif (strpos($column, '(') === false) {
if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) { if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) {
$columns[$i] = $this->driver->quoteColumnName($matches[1]) . ' ' . $matches[2]; $columns[$i] = $driver->quoteColumnName($matches[1]) . ' ' . $matches[2];
} else { } else {
$columns[$i] = $this->driver->quoteColumnName($column); $columns[$i] = $driver->quoteColumnName($column);
} }
} }
} }
...@@ -873,7 +870,7 @@ class QueryBuilder extends \yii\base\Object ...@@ -873,7 +870,7 @@ class QueryBuilder extends \yii\base\Object
if (is_object($column)) { if (is_object($column)) {
$columns[$i] = (string)$column; $columns[$i] = (string)$column;
} elseif (strpos($column, '(') === false) { } elseif (strpos($column, '(') === false) {
$columns[$i] = $this->driver->quoteColumnName($column); $columns[$i] = $this->quoteColumnName($column);
} }
} }
} }
...@@ -890,7 +887,7 @@ class QueryBuilder extends \yii\base\Object ...@@ -890,7 +887,7 @@ class QueryBuilder extends \yii\base\Object
protected function quoteTableName($name, $simple = false) protected function quoteTableName($name, $simple = false)
{ {
if ($this->autoQuote) { if ($this->autoQuote) {
return $simple ? $this->driver->quoteSimpleTableName($name) : $this->driver->quoteTableName($name); return $this->connection->quoteTableName($name, $simple);
} else { } else {
return $name; return $name;
} }
...@@ -906,7 +903,7 @@ class QueryBuilder extends \yii\base\Object ...@@ -906,7 +903,7 @@ class QueryBuilder extends \yii\base\Object
protected function quoteColumnName($name, $simple = false) protected function quoteColumnName($name, $simple = false)
{ {
if ($this->autoQuote) { if ($this->autoQuote) {
return $simple ? $this->driver->quoteSimpleColumnName($name) : $this->driver->quoteColumnName($name); return $this->connection->quoteColumnName($name, $simple);
} else { } else {
return $name; return $name;
} }
......
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