Commit 2188ece1 by Qiang Xue

refactoring DB

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