yii2數據庫查詢操作

首先看findOne的函數定義,該函數定義在BaseActiveRecord當中

return static::findOne(['id' => $id, 'status' => self::STATUS_ACTIVE]);

findOne定義是:

public static function findOne($condition)
{
     return static::findByCondition($condition)->one();
}

也就是說我們需要看一下findByCondition的函數的定義,該函數定義在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函數的定義是在ActiveRecord類中定義的
public static function find()
{     
     return Yii::createObject(ActiveQuery::className(), [get_called_class()]);
}

也就是說$query是一個ActiveQuery的對象,其需要傳入的參數是需要進行查詢的類的名字"User";
中間這一部分,先不要看,因爲還沒時間看,直接看下面的一行addWhere該函數的定義是在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;
}

在這裏僅僅是將傳入的參數$condition,付給ActiveQuery的where成員變量。到此findByCondition已經執行完成,開始執行one函數。該函數定義在ActiveQuery類當中

public function one($db = null)
{
    $row = parent::one($db);
    if ($row !== false) {
        $models = $this->populate([$row]);
        return reset($models) ?: null;
    } else {
        return null;
    }
}

首先調用父類Query的one函數,該函數定義如下:

public function one($db = null)
{
    return $this->createCommand($db)->queryOne();
}

這裏就需要看一下createCommand函數,該函數的定義是:

public function createCommand($db = null)
{
    if ($db === null) {
        $db = Yii::$app->getDb();
    }
    list ($sql, $params) = $db->getQueryBuilder()->build($this);
    return $db->createCommand($sql, $params);
}
這裏可以看到需要獲得數據庫鏈接配置,然後創建queryBuilder對象,並利用build模式構建完整的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]; 
}


好吧,看看這個吧!!!
這一部分使用了build模式,進行創建整個sql語句下面的幾個函數就先不說了,因爲這一部分就是分部分進行構建查詢語句,分部分構建select  from join   where group having 
每個函數都構建了一部分語句,最後各個部分語句形成了$clauses是由各部分語句的數組。最後返回$sql, $param的數組,得到$sql之後可以繼續執行了,,接下來創建$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函數如下:

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;
}
先認爲param是空的吧,這一路跟下來,腦袋都快炸了,接下來要執行的是,yii\db\command類的queryOne函數
public function queryOne($fetchMode = null)
{
    return $this->queryInternal('fetch', $fetchMode);
}
queryInternal函數定義

protected function queryInternal($method, $fetchMode = null)
{
    $rawSql = $this->getRawSql();
    Yii::info($rawSql, 'yii\db\Command::query');
    if ($method !== '') {
       $info = $this->db->getQueryCacheInfo($this->queryCacheDuration, $this->queryCacheDependency);
       if (is_array($info)) {
           /* @var $cache \yii\caching\Cache */
          $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', 'yii\db\Command::query');
                return $result[0];
            }
        }
    }

    $this->prepare(true);
    $token = $rawSql;
   try {
       Yii::beginProfile($token, 'yii\db\Command::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, 'yii\db\Command::query');
    } catch (\Exception $e) {
        Yii::endProfile($token, 'yii\db\Command::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', 'yii\db\Command::query');
    }
    return $result;
}
這樣看來,就是講所有的查詢結果進行返回,那麼會有人問不是findOne嗎?在哪裏進行的取one操作呢?是在ActiveQuery的one操作中,父類得到所有的查詢結果,子類將查詢結果進行reset操作,將第一行記錄返回

查詢總覽:
所有的都是這樣查詢的static::findOne(條件),findOne函數定義是在BaseActiveRecord類中定義的,因爲當前使用的User類的父類是ActiveRecord而ActiveRecord的父類是BaseActiveRecord
在此調用的是findByCondition在該函數中獲得了ActiveQuery類的對象,同時傳入需要修改的類的名稱(需要調用該類的靜態函數tableName,獲得表名稱),並且在findByCondition中將條件數組轉成
String,方便繼續使用,繼續調用ActiveQuery的addWhere記錄下ActiveQuery的where條件,之後調用Query的one()函數,在Query的one函數中創建了yii\db\Connection類的對象,繼續調用該db對象的getQueryBuilder函數,創建了一個QueryBuilder對象,然後在QUeryBuilder對象中進行完整的sql構造,將sql查詢語句中可能出現的各個子查詢都進行分別處理,各部分處理結果得到一個字符串,將這部分字符串組成數組,在展開implode,合成一個sql語句,將生成的sql語句傳遞給由yii\db\Connection創建的yii\db\command對象,並進行執行queryOne函數該函數調用queryInternal並返回結果集,最後由ActiveQuery的one函數進行reset操作,返回第一個結果記錄






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章