yii2數(shù)據(jù)庫(kù)查詢操作
首先看findOne的函數(shù)定義,該函數(shù)定義在BaseActiveRecord當(dāng)中
return?static::findOne(['id'?=>?$id,?'status'?=>?self::STATUS_ACTIVE]);
findOne定義是:
public?static?function?findOne($condition)
{
?????return?static::findByCondition($condition)->one();
}
也就是說(shuō)我們需要看一下findByCondition的函數(shù)的定義,該函數(shù)定義在BaseActiveRecord
protected?static?function?findByCondition($condition)
{
????$query?=?static::find();
????if?(!ArrayHelper::isAssociative($condition))?{
?????//?query?by?primary?key
???????$primaryKey?=?static::primaryKey();
???????if?(isset($primaryKey[0]))?{
??????????$condition?=?[$primaryKey[0]?=>?$condition];
????????}?else?{
??????????throw?new?InvalidConfigException('"'?.?get_called_class()?.?'"?must?have?a?primary?key.');
????????}
????}
????return?$query->andWhere($condition);
}
find函數(shù)的定義是在ActiveRecord類中定義的
public?static?function?find()
{?????
?????return?Yii::createObject(ActiveQuery::className(),?[get_called_class()]);
}
也就是說(shuō)$query是一個(gè)ActiveQuery的對(duì)象,其需要傳入的參數(shù)是需要進(jìn)行查詢的類的名字"User";
中間這一部分,先不要看,因?yàn)檫€沒(méi)時(shí)間看,直接看下面的一行addWhere該函數(shù)的定義是在Query類中
public?function?andWhere($condition,?$params?=?[])
{
????if?($this->where?===?null)?{
???????$this->where?=?$condition;
????}?else?{
???????$this->where?=?['and',?$this->where,?$condition];
????}
????$this->addParams($params);
????return?$this;
}
在這里僅僅是將傳入的參數(shù)$condition,付給ActiveQuery的where成員變量。到此findByCondition已經(jīng)執(zhí)行完成,開始執(zhí)行one函數(shù)。該函數(shù)定義在ActiveQuery類當(dāng)中
public?function?one($db?=?null)
{
????$row?=?parent::one($db);
????if?($row?!==?false)?{
????????$models?=?$this->populate([$row]);
????????return?reset($models)??:?null;
????}?else?{
????????return?null;
????}
}
首先調(diào)用父類Query的one函數(shù),該函數(shù)定義如下:
public?function?one($db?=?null)
{
????return?$this->createCommand($db)->queryOne();
}
這里就需要看一下createCommand函數(shù),該函數(shù)的定義是:
public?function?createCommand($db?=?null)
{
????if?($db?===?null)?{
????????$db?=?Yii::$app->getDb();
????}
????list?($sql,?$params)?=?$db->getQueryBuilder()->build($this);
????return?$db->createCommand($sql,?$params);
}這里可以看到需要獲得數(shù)據(jù)庫(kù)鏈接配置,然后創(chuàng)建queryBuilder對(duì)象,并利用build模式構(gòu)建完整的sql語(yǔ)句
public?function?build($query,?$params?=?[])
{
???$query?=?$query->prepare($this);
???$params?=?empty($params)???$query->params?:?array_merge($params,?$query->params);
???$clauses?=?[
??????$this->buildSelect($query->select,?$params,?$query->distinct,?$query->selectOption),
??????$this->buildFrom($query->from,?$params),
??????$this->buildJoin($query->join,?$params),
??????$this->buildWhere($query->where,?$params),
??????$this->buildGroupBy($query->groupBy),
??????$this->buildHaving($query->having,?$params),
????];
????$sql?=?implode($this->separator,?array_filter($clauses));
????$sql?=?$this->buildOrderByAndLimit($sql,?$query->orderBy,?$query->limit,?$query->offset);
????if?(!empty($query->orderBy))?{
????????foreach?($query->orderBy?as?$expression)?{
????????if?($expression?instanceof?Expression)?{
??????????$params?=?array_merge($params,?$expression->params);??
????????????}
????????}
????}
????if?(!empty($query->groupBy))?{??
??????foreach?($query->groupBy?as?$expression)?{
?????????if?($expression?instanceof?Expression)?{
????????????$params?=?array_merge($params,?$expression->params);
????????????}
????????}
????}
????$union?=?$this->buildUnion($query->union,?$params);
????if?($union?!==?'')?{
????????$sql?=?"($sql){$this->separator}$union";
????}
????return?[$sql,?$params];?
}
好吧,看看這個(gè)吧?。。?這一部分使用了build模式,進(jìn)行創(chuàng)建整個(gè)sql語(yǔ)句下面的幾個(gè)函數(shù)就先不說(shuō)了,因?yàn)檫@一部分就是分部分進(jìn)行構(gòu)建查詢語(yǔ)句,分部分構(gòu)建select ?from join ?
where group having?
每個(gè)函數(shù)都構(gòu)建了一部分語(yǔ)句,最后各個(gè)部分語(yǔ)句形成了$clauses是由各部分語(yǔ)句的數(shù)組。最后返回$sql, $param的數(shù)組,得到$sql之后可以繼續(xù)執(zhí)行了,,接下來(lái)創(chuàng)建$command
return?$db->createCommand($sql,?$params);
public?function?createCommand($sql?=?null,?$params?=?[])
{
????/**?@var?Command?$command?*/
????$command?=?new?$this->commandClass([
?????'db'?=>?$this,
??????'sql'?=>?$sql,
????]);
????return?$command->bindValues($params);
}
bindValues函數(shù)如下:
public?function?bindValues($values)
{
????if?(empty($values))?{
?????return?$this;
????}
????$schema?=?$this->db->getSchema();
????foreach?($values?as?$name?=>?$value)?{
???????if?(is_array($value))?{
?????????$this->_pendingParams[$name]?=?$value;
?????????$this->params[$name]?=?$value[0];
???????}?else?{
?????????$type?=?$schema->getPdoType($value);
?????????$this->_pendingParams[$name]?=?[$value,?$type];
?????????$this->params[$name]?=?$value;
????????}
????}
????return?$this;
}先認(rèn)為param是空的吧,這一路跟下來(lái),腦袋都快炸了,接下來(lái)要執(zhí)行的是,yiidbcommand類的queryOne函數(shù)
public?function?queryOne($fetchMode?=?null)
{
????return?$this->queryInternal('fetch',?$fetchMode);
}queryInternal函數(shù)定義
protected?function?queryInternal($method,?$fetchMode?=?null)
{
????$rawSql?=?$this->getRawSql();
????Yii::info($rawSql,?'yiidbCommand::query');
????if?($method?!==?'')?{
???????$info?=?$this->db->getQueryCacheInfo($this->queryCacheDuration,?$this->queryCacheDependency);
???????if?(is_array($info))?{
???????????/*?@var?$cache?yiicachingCache?*/
??????????$cache?=?$info[0];
??????????$cacheKey?=?[
??????????__CLASS__,
??????????$method,
??????????$fetchMode,
??????????$this->db->dsn,
??????????$this->db->username,
??????????$rawSql,?
????????????];
??????????$result?=?$cache->get($cacheKey);
??????????if?(is_array($result)?&&?isset($result[0]))?{
????????????????Yii::trace('Query?result?served?from?cache',?'yiidbCommand::query');
????????????????return?$result[0];
????????????}
????????}
????}
????$this->prepare(true);
????$token?=?$rawSql;
???try?{
???????Yii::beginProfile($token,?'yiidbCommand::query');
???????$this->pdoStatement->execute();
???????if?($method?===?'')?{
???????????$result?=?new?DataReader($this);
????????}?else?{
???????????if?($fetchMode?===?null)?{
??????????????$fetchMode?=?$this->fetchMode;
????????????}
???????????$result?=?call_user_func_array([$this->pdoStatement,?$method],?(array)?$fetchMode);
???????????$this->pdoStatement->closeCursor();
????????}
????????Yii::endProfile($token,?'yiidbCommand::query');
????}?catch?(Exception?$e)?{
????????Yii::endProfile($token,?'yiidbCommand::query');
????????throw?$this->db->getSchema()->convertException($e,?$rawSql);
????}
????if?(isset($cache,?$cacheKey,?$info))?{
????????$cache->set($cacheKey,?[$result],?$info[1],?$info[2]);
????????Yii::trace('Saved?query?result?in?cache',?'yiidbCommand::query');
????}
????return?$result;
}這樣看來(lái),就是講所有的查詢結(jié)果進(jìn)行返回,那么會(huì)有人問(wèn)不是findOne嗎?在哪里進(jìn)行的取one操作呢?是在ActiveQuery的one操作中,父類得到所有的查詢結(jié)果,子類將查詢結(jié)果進(jìn)行reset操作,將第一行記錄返回
查詢總覽:
所有的都是這樣查詢的static::findOne(條件),findOne函數(shù)定義是在BaseActiveRecord類中定義的,因?yàn)楫?dāng)前使用的User類的父類是ActiveRecord而ActiveRecord的父類是BaseActiveRecord
在此調(diào)用的是findByCondition在該函數(shù)中獲得了ActiveQuery類的對(duì)象,同時(shí)傳入需要修改的類的名稱(需要調(diào)用該類的靜態(tài)函數(shù)tableName,獲得表名稱),并且在findByCondition中將條件數(shù)組轉(zhuǎn)成
String,方便繼續(xù)使用,繼續(xù)調(diào)用ActiveQuery的addWhere記錄下ActiveQuery的where條件,之后調(diào)用Query的one()函數(shù),在Query的one函數(shù)中創(chuàng)建了yiidbConnection類的對(duì)象,繼續(xù)調(diào)用該db對(duì)象的getQueryBuilder函數(shù),創(chuàng)建了一個(gè)QueryBuilder對(duì)象,然后在QUeryBuilder對(duì)象中進(jìn)行完整的sql構(gòu)造,將sql查詢語(yǔ)句中可能出現(xiàn)的各個(gè)子查詢都進(jìn)行分別處理,各部分處理結(jié)果得到一個(gè)字符串,將這部分字符串組成數(shù)組,在展開implode,合成一個(gè)sql語(yǔ)句,將生成的sql語(yǔ)句傳遞給由yiidbConnection創(chuàng)建的yiidbcommand對(duì)象,并進(jìn)行執(zhí)行queryOne函數(shù)該函數(shù)調(diào)用queryInternal并返回結(jié)果集,最后由ActiveQuery的one函數(shù)進(jìn)行reset操作,返回第一個(gè)結(jié)果記錄





