Commit 2188ece1 by Qiang Xue

refactoring DB

parent 9cd67bb6
...@@ -13,12 +13,11 @@ namespace yii\db; ...@@ -13,12 +13,11 @@ namespace yii\db;
use yii\db\Connection; use yii\db\Connection;
use yii\db\Command; use yii\db\Command;
use yii\db\QueryBuilder; use yii\db\QueryBuilder;
use yii\db\BaseQuery;
use yii\base\VectorIterator; use yii\base\VectorIterator;
use yii\db\Expression; use yii\db\Expression;
use yii\db\Exception; use yii\db\Exception;
class ActiveQuery extends BaseQuery class ActiveQuery extends Query
{ {
/** /**
* @var string the name of the ActiveRecord class. * @var string the name of the ActiveRecord class.
...@@ -125,13 +124,17 @@ class ActiveQuery extends BaseQuery ...@@ -125,13 +124,17 @@ class ActiveQuery extends BaseQuery
/** /**
* Creates a DB command that can be used to execute this query. * Creates a DB command that can be used to execute this query.
* @param Connection $db the DB connection used to create the DB command.
* If null, the DB connection returned by [[modelClass]] will be used.
* @return Command the created DB command instance. * @return Command the created DB command instance.
*/ */
public function createCommand() public function createCommand($db = null)
{ {
/** @var $modelClass ActiveRecord */ /** @var $modelClass ActiveRecord */
$modelClass = $this->modelClass; $modelClass = $this->modelClass;
if ($db === null) {
$db = $modelClass::getDbConnection(); $db = $modelClass::getDbConnection();
}
if ($this->sql === null) { if ($this->sql === null) {
if ($this->from === null) { if ($this->from === null) {
$tableName = $modelClass::tableName(); $tableName = $modelClass::tableName();
......
...@@ -96,9 +96,11 @@ class ActiveRelation extends ActiveQuery ...@@ -96,9 +96,11 @@ class ActiveRelation extends ActiveQuery
/** /**
* Creates a DB command that can be used to execute this query. * Creates a DB command that can be used to execute this query.
* @param Connection $db the DB connection used to create the DB command.
* If null, the DB connection returned by [[modelClass]] will be used.
* @return Command the created DB command instance. * @return Command the created DB command instance.
*/ */
public function createCommand() public function createCommand($db = null)
{ {
if ($this->primaryModel !== null) { if ($this->primaryModel !== null) {
// lazy loading // lazy loading
...@@ -120,7 +122,7 @@ class ActiveRelation extends ActiveQuery ...@@ -120,7 +122,7 @@ class ActiveRelation extends ActiveQuery
$this->filterByModels(array($this->primaryModel)); $this->filterByModels(array($this->primaryModel));
} }
} }
return parent::createCommand(); return parent::createCommand($db);
} }
public function findWith($name, &$primaryModels) public function findWith($name, &$primaryModels)
......
...@@ -12,7 +12,7 @@ namespace yii\db; ...@@ -12,7 +12,7 @@ namespace yii\db;
use yii\db\Exception; use yii\db\Exception;
/** /**
* QueryBuilder builds a SELECT SQL statement based on the specification given as a [[BaseQuery]] object. * QueryBuilder builds a SELECT SQL statement based on the specification given as a [[Query]] object.
* *
* QueryBuilder can also be used to build SQL statements such as INSERT, UPDATE, DELETE, CREATE TABLE, * QueryBuilder can also be used to build SQL statements such as INSERT, UPDATE, DELETE, CREATE TABLE,
* from a [[Query]] object. * from a [[Query]] object.
...@@ -41,10 +41,6 @@ class QueryBuilder extends \yii\base\Object ...@@ -41,10 +41,6 @@ class QueryBuilder extends \yii\base\Object
* Child classes should override this property to declare supported type mappings. * Child classes should override this property to declare supported type mappings.
*/ */
public $typeMap = array(); public $typeMap = array();
/**
* @var Query the Query object that is currently being processed by the query builder to generate a SQL statement.
*/
public $query;
/** /**
* Constructor. * Constructor.
...@@ -58,8 +54,8 @@ class QueryBuilder extends \yii\base\Object ...@@ -58,8 +54,8 @@ class QueryBuilder extends \yii\base\Object
} }
/** /**
* Generates a SELECT SQL statement from a [[BaseQuery]] object. * Generates a SELECT SQL statement from a [[Query]] object.
* @param BaseQuery $query the [[Query]] object from which the SQL statement will be generated * @param Query $query the [[Query]] object from which the SQL statement will be generated
* @return string the generated SQL statement * @return string the generated SQL statement
*/ */
public function build($query) public function build($query)
...@@ -79,27 +75,29 @@ class QueryBuilder extends \yii\base\Object ...@@ -79,27 +75,29 @@ class QueryBuilder extends \yii\base\Object
} }
/** /**
* Creates and executes an INSERT SQL statement. * Creates an INSERT SQL statement.
* The method will properly escape the column names, and bind the values to be inserted.
* For example, * For example,
* *
* ~~~ * ~~~
* $sql = $queryBuilder->insert('tbl_user', array( * $sql = $queryBuilder->insert('tbl_user', array(
* 'name' => 'Sam', * 'name' => 'Sam',
* 'age' => 30, * 'age' => 30,
* )); * ), $params);
* ~~~ * ~~~
* *
* The method will properly escape the table and column names.
*
* @param string $table the table that new rows will be inserted into. * @param string $table the table that new rows will be inserted into.
* @param array $columns the column data (name=>value) to be inserted into the table. * @param array $columns the column data (name=>value) to be inserted into the table.
* @param array $params the binding parameters that will be generated by this method.
* They should be bound to the DB command later.
* @return string the INSERT SQL * @return string the INSERT SQL
*/ */
public function insert($table, $columns) public function insert($table, $columns, &$params)
{ {
$names = array(); $names = array();
$placeholders = array(); $placeholders = array();
$count = 0; $count = 0;
$params = array();
foreach ($columns as $name => $value) { foreach ($columns as $name => $value) {
$names[] = $this->quoteColumnName($name); $names[] = $this->quoteColumnName($name);
if ($value instanceof Expression) { if ($value instanceof Expression) {
...@@ -113,9 +111,6 @@ class QueryBuilder extends \yii\base\Object ...@@ -113,9 +111,6 @@ class QueryBuilder extends \yii\base\Object
$count++; $count++;
} }
} }
if ($this->query instanceof BaseQuery) {
$this->query->addParams($params);
}
return 'INSERT INTO ' . $this->quoteTableName($table) return 'INSERT INTO ' . $this->quoteTableName($table)
. ' (' . implode(', ', $names) . ') VALUES (' . ' (' . implode(', ', $names) . ') VALUES ('
...@@ -123,8 +118,7 @@ class QueryBuilder extends \yii\base\Object ...@@ -123,8 +118,7 @@ class QueryBuilder extends \yii\base\Object
} }
/** /**
* Creates and executes an UPDATE SQL statement. * Creates an UPDATE SQL statement.
* The method will properly escape the column names and bind the values to be updated.
* For example, * For example,
* *
* ~~~ * ~~~
...@@ -134,14 +128,17 @@ class QueryBuilder extends \yii\base\Object ...@@ -134,14 +128,17 @@ class QueryBuilder extends \yii\base\Object
* ), 'age > 30', $params); * ), 'age > 30', $params);
* ~~~ * ~~~
* *
* The method will properly escape the table and column names.
*
* @param string $table the table to be updated. * @param string $table the table to be updated.
* @param array $columns the column data (name=>value) to be updated. * @param array $columns the column data (name=>value) to be updated.
* @param mixed $condition the condition that will be put in the WHERE part. Please * @param mixed $condition the condition that will be put in the WHERE part. Please
* refer to [[Query::where()]] on how to specify condition. * refer to [[Query::where()]] on how to specify condition.
* @param array $params the parameters to be bound to the query. * @param array $params the binding parameters that will be modified by this method
* so that they can be bound to the DB command later.
* @return string the UPDATE SQL * @return string the UPDATE SQL
*/ */
public function update($table, $columns, $condition = '', $params = array()) public function update($table, $columns, $condition = '', &$params)
{ {
$lines = array(); $lines = array();
$count = 0; $count = 0;
...@@ -157,9 +154,6 @@ class QueryBuilder extends \yii\base\Object ...@@ -157,9 +154,6 @@ class QueryBuilder extends \yii\base\Object
$count++; $count++;
} }
} }
if ($this->query instanceof BaseQuery) {
$this->query->addParams($params);
}
$sql = 'UPDATE ' . $this->quoteTableName($table) . ' SET ' . implode(', ', $lines); $sql = 'UPDATE ' . $this->quoteTableName($table) . ' SET ' . implode(', ', $lines);
if (($where = $this->buildCondition($condition)) !== '') { if (($where = $this->buildCondition($condition)) !== '') {
$sql .= ' WHERE ' . $where; $sql .= ' WHERE ' . $where;
...@@ -169,28 +163,26 @@ class QueryBuilder extends \yii\base\Object ...@@ -169,28 +163,26 @@ class QueryBuilder extends \yii\base\Object
} }
/** /**
* Creates and executes a DELETE SQL statement. * Creates a DELETE SQL statement.
* For example, * For example,
* *
* ~~~ * ~~~
* $sql = $queryBuilder->delete('tbl_user', 'status = 0'); * $sql = $queryBuilder->delete('tbl_user', 'status = 0');
* ~~~ * ~~~
* *
* The method will properly escape the table and column names.
*
* @param string $table the table where the data will be deleted from. * @param string $table the table where the data will be deleted from.
* @param mixed $condition the condition that will be put in the WHERE part. Please * @param mixed $condition the condition that will be put in the WHERE part. Please
* refer to [[Query::where()]] on how to specify condition. * refer to [[Query::where()]] on how to specify condition.
* @param array $params the parameters to be bound to the query.
* @return string the DELETE SQL * @return string the DELETE SQL
*/ */
public function delete($table, $condition = '', $params = array()) public function delete($table, $condition = '')
{ {
$sql = 'DELETE FROM ' . $this->quoteTableName($table); $sql = 'DELETE FROM ' . $this->quoteTableName($table);
if (($where = $this->buildCondition($condition)) !== '') { if (($where = $this->buildCondition($condition)) !== '') {
$sql .= ' WHERE ' . $where; $sql .= ' WHERE ' . $where;
} }
if ($params !== array() && $this->query instanceof BaseQuery) {
$this->query->addParams($params);
}
return $sql; return $sql;
} }
...@@ -461,7 +453,7 @@ class QueryBuilder extends \yii\base\Object ...@@ -461,7 +453,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 [[BaseQuery::where()]] * @param string|array $condition the condition specification. Please refer to [[Query::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
...@@ -878,7 +870,7 @@ class QueryBuilder extends \yii\base\Object ...@@ -878,7 +870,7 @@ class QueryBuilder extends \yii\base\Object
$unions = array($unions); $unions = array($unions);
} }
foreach ($unions as $i => $union) { foreach ($unions as $i => $union) {
if ($union instanceof BaseQuery) { if ($union instanceof Query) {
$unions[$i] = $this->build($union); $unions[$i] = $this->build($union);
} }
} }
......
...@@ -36,12 +36,6 @@ class ExistValidator extends Validator ...@@ -36,12 +36,6 @@ class ExistValidator extends Validator
*/ */
public $attributeName; public $attributeName;
/** /**
* @var \yii\db\BaseQuery additional query criteria. This will be combined
* with the condition that checks if the attribute value exists in the
* corresponding table column.
*/
public $query = null;
/**
* @var boolean whether the attribute value can be null or empty. Defaults to true, * @var boolean whether the attribute value can be null or empty. Defaults to true,
* meaning that if the attribute is empty, it is considered valid. * meaning that if the attribute is empty, it is considered valid.
*/ */
...@@ -63,20 +57,17 @@ class ExistValidator extends Validator ...@@ -63,20 +57,17 @@ class ExistValidator extends Validator
return; return;
} }
/** @var $className \yii\db\ActiveRecord */
$className = ($this->className === null) ? get_class($object) : \Yii::import($this->className); $className = ($this->className === null) ? get_class($object) : \Yii::import($this->className);
$attributeName = ($this->attributeName === null) ? $attribute : $this->attributeName; $attributeName = ($this->attributeName === null) ? $attribute : $this->attributeName;
$table = $object::getMetaData()->table; $table = $className::getTableSchema();
if (($column = $table->getColumn($attributeName)) === null) { if (($column = $table->getColumn($attributeName)) === null) {
throw new \yii\base\Exception('Table "' . $table->name . '" does not have a column named "' . $attributeName . '"'); throw new \yii\base\Exception('Table "' . $table->name . '" does not have a column named "' . $attributeName . '"');
} }
$finder = $object->find()->where(array($column->name => $value)); $query = $className::find();
$query->where(array($column->name => $value));
if ($this->query instanceof \yii\db\BaseQuery) { if (!$query->exists()) {
$finder->mergeWith($this->query);
}
if (!$finder->exists()) {
$message = ($this->message !== null) ? $this->message : \Yii::t('yii', '{attribute} "{value}" is invalid.'); $message = ($this->message !== null) ? $this->message : \Yii::t('yii', '{attribute} "{value}" is invalid.');
$this->addError($object, $attribute, $message, array('{value}' => $value)); $this->addError($object, $attribute, $message, array('{value}' => $value));
} }
......
...@@ -30,8 +30,8 @@ class UniqueValidator extends Validator ...@@ -30,8 +30,8 @@ class UniqueValidator extends Validator
/** /**
* @var string the yii\db\ActiveRecord class name or alias of the class * @var string the yii\db\ActiveRecord class name or alias of the class
* that should be used to look for the attribute value being validated. * that should be used to look for the attribute value being validated.
* Defaults to null, meaning using the class of the object currently * Defaults to null, meaning using the yii\db\ActiveRecord class of
* being validated. * the attribute being validated.
* @see attributeName * @see attributeName
*/ */
public $className; public $className;
...@@ -39,16 +39,9 @@ class UniqueValidator extends Validator ...@@ -39,16 +39,9 @@ class UniqueValidator extends Validator
* @var string the ActiveRecord class attribute name that should be * @var string the ActiveRecord class attribute name that should be
* used to look for the attribute value being validated. Defaults to null, * used to look for the attribute value being validated. Defaults to null,
* meaning using the name of the attribute being validated. * meaning using the name of the attribute being validated.
* @see className
*/ */
public $attributeName; public $attributeName;
/** /**
* @var \yii\db\ActiveQuery additional query criteria. This will be
* combined with the condition that checks if the attribute value exists
* in the corresponding table column.
*/
public $query = null;
/**
* @var string the user-defined error message. The placeholders "{attribute}" and "{value}" * @var string the user-defined error message. The placeholders "{attribute}" and "{value}"
* are recognized, which will be replaced with the actual attribute name and value, respectively. * are recognized, which will be replaced with the actual attribute name and value, respectively.
*/ */
...@@ -59,13 +52,11 @@ class UniqueValidator extends Validator ...@@ -59,13 +52,11 @@ class UniqueValidator extends Validator
*/ */
public $skipOnError = true; public $skipOnError = true;
/** /**
* Validates the attribute of the object. * Validates the attribute of the object.
* If there is any error, the error message is added to the object. * If there is any error, the error message is added to the object.
* @param \yiiunit\data\ar\ActiveRecord $object the object being validated * @param \yii\db\ActiveRecord $object the object being validated
* @param string $attribute the attribute being validated * @param string $attribute the attribute being validated
*
* @throws \yii\base\Exception if table doesn't have column specified * @throws \yii\base\Exception if table doesn't have column specified
*/ */
public function validateAttribute($object, $attribute) public function validateAttribute($object, $attribute)
...@@ -75,30 +66,27 @@ class UniqueValidator extends Validator ...@@ -75,30 +66,27 @@ class UniqueValidator extends Validator
return; return;
} }
/** @var $className \yii\db\ActiveRecord */
$className = ($this->className === null) ? get_class($object) : \Yii::import($this->className); $className = ($this->className === null) ? get_class($object) : \Yii::import($this->className);
$attributeName = ($this->attributeName === null) ? $attribute : $this->attributeName; $attributeName = ($this->attributeName === null) ? $attribute : $this->attributeName;
$table = $object::getMetaData()->table; $table = $className::getTableSchema();
if (($column = $table->getColumn($attributeName)) === null) { if (($column = $table->getColumn($attributeName)) === null) {
throw new \yii\base\Exception('Table "' . $table->name . '" does not have a column named "' . $attributeName . '"'); throw new \yii\base\Exception('Table "' . $table->name . '" does not have a column named "' . $attributeName . '"');
} }
$finder = $object::find(); $query = $className::find();
$finder->where($this->caseSensitive ? "{$column->quotedName}=:value" : "LOWER({$column->quotedName})=LOWER(:value)"); $query->where($this->caseSensitive ? "{$column->quotedName}=:value" : "LOWER({$column->quotedName})=LOWER(:value)");
$finder->params(array(':value' => $value)); $query->params(array(':value' => $value));
if ($this->query instanceof \yii\db\BaseQuery) {
$finder->mergeWith($this->query);
}
if ($object->getIsNewRecord()) { if ($object->getIsNewRecord()) {
// if current $object isn't in the database yet then it's OK just // if current $object isn't in the database yet then it's OK just
// to call exists() // to call exists()
$exists = $finder->exists(); $exists = $query->exists();
} else { } else {
// if current $object is in the database already we can't use exists() // if current $object is in the database already we can't use exists()
$finder->limit(2); $query->limit(2);
$objects = $finder->all(); $objects = $query->all();
$n = count($objects); $n = count($objects);
if ($n === 1) { if ($n === 1) {
......
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