yii2數(shù)據(jù)庫查詢操作
首先看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(); }
也就是說我們需要看一下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()]); }
也就是說$query是一個(gè)ActiveQuery的對象,其需要傳入的參數(shù)是需要進(jìn)行查詢的類的名字"User";
中間這一部分,先不要看,因?yàn)檫€沒時(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ù)庫鏈接配置,然后創(chuàng)建queryBuilder對象,并利用build模式構(gòu)建完整的sql語句
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語句下面的幾個(gè)函數(shù)就先不說了,因?yàn)檫@一部分就是分部分進(jìn)行構(gòu)建查詢語句,分部分構(gòu)建select ?from join ?
where group having?
每個(gè)函數(shù)都構(gòu)建了一部分語句,最后各個(gè)部分語句形成了$clauses是由各部分語句的數(shù)組。最后返回$sql, $param的數(shù)組,得到$sql之后可以繼續(xù)執(zhí)行了,,接下來創(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是空的吧,這一路跟下來,腦袋都快炸了,接下來要執(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; }
這樣看來,就是講所有的查詢結(jié)果進(jìn)行返回,那么會(huì)有人問不是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類的對象,同時(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類的對象,繼續(xù)調(diào)用該db對象的getQueryBuilder函數(shù),創(chuàng)建了一個(gè)QueryBuilder對象,然后在QUeryBuilder對象中進(jìn)行完整的sql構(gòu)造,將sql查詢語句中可能出現(xiàn)的各個(gè)子查詢都進(jìn)行分別處理,各部分處理結(jié)果得到一個(gè)字符串,將這部分字符串組成數(shù)組,在展開implode,合成一個(gè)sql語句,將生成的sql語句傳遞給由yiidbConnection創(chuàng)建的yiidbcommand對象,并進(jìn)行執(zhí)行queryOne函數(shù)該函數(shù)調(diào)用queryInternal并返回結(jié)果集,最后由ActiveQuery的one函數(shù)進(jìn)行reset操作,返回第一個(gè)結(jié)果記錄