YII2框架查询源码解析

首先看findOne的函数定义,该函数定义在BaseActiveRecord当中

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

findOne定义是:
  1. public static function findOne($condition)  
  2. {  
  3.      return static::findByCondition($condition)->one();  
  4. }  

也就是说我们需要看一下findByCondition的函数的定义,该函数定义在BaseActiveRecord

  1. protected static function findByCondition($condition)  
  2. {  
  3.     $query = static::find();  
  4.   
  5.     if (!ArrayHelper::isAssociative($condition)) {  
  6.      // query by primary key  
  7.        $primaryKey = static::primaryKey();  
  8.        if (isset($primaryKey[0])) {  
  9.           $condition = [$primaryKey[0] => $condition];  
  10.         } else {  
  11.           throw new InvalidConfigException('"' . get_called_class() . '" must have a primary key.');  
  12.         }  
  13.     }  
  14.     return $query->andWhere($condition);  
  15. }  

find函数的定义是在ActiveRecord类中定义的
  1. public static function find()  
  2. {       
  3.      return Yii::createObject(ActiveQuery::className(), [get_called_class()]);  
  4. }  

也就是说$query是一个ActiveQuery的对象,其需要传入的参数是需要进行查询的类的名字"User";
中间这一部分,先不要看,因为还没时间看,直接看下面的一行addWhere该函数的定义是在Query类中
  1. public function andWhere($condition$params = [])  
  2. {  
  3.     if ($this->where === null) {  
  4.        $this->where = $condition;  
  5.     } else {  
  6.        $this->where = ['and'$this->where, $condition];  
  7.     }  
  8.     $this->addParams($params);  
  9.     return $this;  
  10. }  

在这里仅仅是将传入的参数$condition,付给ActiveQuery的where成员变量。到此findByCondition已经执行完成,开始执行one函数。该函数定义在ActiveQuery类当中

  1. public function one($db = null)  
  2. {  
  3.     $row = parent::one($db);  
  4.     if ($row !== false) {  
  5.         $models = $this->populate([$row]);  
  6.         return reset($models) ?: null;  
  7.     } else {  
  8.         return null;  
  9.     }  
  10. }  

首先调用父类Query的one函数,该函数定义如下:

  1. public function one($db = null)  
  2. {  
  3.     return $this->createCommand($db)->queryOne();  
  4. }  

这里就需要看一下createCommand函数,该函数的定义是:

  1. public function createCommand($db = null)  
  2. {  
  3.     if ($db === null) {  
  4.         $db = Yii::$app->getDb();  
  5.     }  
  6.     list ($sql$params) = $db->getQueryBuilder()->build($this);  
  7.     return $db->createCommand($sql$params);  
  8. }  
这里可以看到需要获得数据库链接配置,然后创建queryBuilder对象,并利用build模式构建完整的sql语句

  1. public function build($query$params = [])  
  2. {  
  3.    $query = $query->prepare($this);  
  4.   
  5.    $params = empty($params) ? $query->params : array_merge($params$query->params);  
  6.   
  7.    $clauses = [  
  8.       $this->buildSelect($query->select, $params$query->distinct, $query->selectOption),  
  9.       $this->buildFrom($query->from, $params),  
  10.       $this->buildJoin($query->join, $params),  
  11.       $this->buildWhere($query->where, $params),  
  12.       $this->buildGroupBy($query->groupBy),  
  13.       $this->buildHaving($query->having, $params),  
  14.     ];  
  15.     $sql = implode($this->separator, array_filter($clauses));  
  16.     $sql = $this->buildOrderByAndLimit($sql$query->orderBy, $query->limit, $query->offset);  
  17.     if (!empty($query->orderBy)) {  
  18.         foreach ($query->orderBy as $expression) {  
  19.         if ($expression instanceof Expression) {  
  20.           $params = array_merge($params$expression->params);    
  21.             }  
  22.         }  
  23.     }  
  24.     if (!empty($query->groupBy)) {    
  25.       foreach ($query->groupBy as $expression) {  
  26.          if ($expression instanceof Expression) {  
  27.             $params = array_merge($params$expression->params);  
  28.             }  
  29.         }  
  30.     }  
  31.   
  32.     $union = $this->buildUnion($query->union, $params);  
  33.     if ($union !== '') {  
  34.         $sql = "($sql){$this->separator}$union";  
  35.     }  
  36.     return [$sql$params];   
  37. }  


好吧,看看这个吧!!!
这一部分使用了build模式,进行创建整个sql语句下面的几个函数就先不说了,因为这一部分就是分部分进行构建查询语句,分部分构建select  from join   where group having 
每个函数都构建了一部分语句,最后各个部分语句形成了$clauses是由各部分语句的数组。最后返回$sql, $param的数组,得到$sql之后可以继续执行了,,接下来创建$command
  1. return $db->createCommand($sql$params);  

  1. public function createCommand($sql = null, $params = [])  
  2. {  
  3.     /** @var Command $command */  
  4.     $command = new $this->commandClass([  
  5.      'db' => $this,  
  6.       'sql' => $sql,  
  7.     ]);  
  8.   
  9.     return $command->bindValues($params);  
  10. }  

bindValues函数如下:
  1. public function bindValues($values)  
  2. {  
  3.     if (empty($values)) {  
  4.      return $this;  
  5.     }  
  6.   
  7.     $schema = $this->db->getSchema();  
  8.     foreach ($values as $name => $value) {  
  9.        if (is_array($value)) {  
  10.          $this->_pendingParams[$name] = $value;  
  11.          $this->params[$name] = $value[0];  
  12.        } else {  
  13.          $type = $schema->getPdoType($value);  
  14.          $this->_pendingParams[$name] = [$value$type];  
  15.          $this->params[$name] = $value;  
  16.         }  
  17.     }  
  18.     return $this;  
  19. }  
先认为param是空的吧,这一路跟下来,脑袋都快炸了,接下来要执行的是,yii\db\command类的queryOne函数
  1. public function queryOne($fetchMode = null)  
  2. {  
  3.     return $this->queryInternal('fetch'$fetchMode);  
  4. }  
queryInternal函数定义

  1. protected function queryInternal($method$fetchMode = null)  
  2. {  
  3.     $rawSql = $this->getRawSql();  
  4.     Yii::info($rawSql'yii\db\Command::query');  
  5.     if ($method !== '') {  
  6.        $info = $this->db->getQueryCacheInfo($this->queryCacheDuration, $this->queryCacheDependency);  
  7.        if (is_array($info)) {  
  8.            /* @var $cache \yii\caching\Cache */  
  9.           $cache = $info[0];  
  10.           $cacheKey = [  
  11.           __CLASS__,  
  12.           $method,  
  13.           $fetchMode,  
  14.           $this->db->dsn,  
  15.           $this->db->username,  
  16.           $rawSql,   
  17.             ];  
  18.           $result = $cache->get($cacheKey);  
  19.           if (is_array($result) && isset($result[0])) {  
  20.                 Yii::trace('Query result served from cache''yii\db\Command::query');  
  21.                 return $result[0];  
  22.             }  
  23.         }  
  24.     }  
  25.   
  26.     $this->prepare(true);  
  27.     $token = $rawSql;  
  28.    try {  
  29.        Yii::beginProfile($token'yii\db\Command::query');  
  30.        $this->pdoStatement->execute();  
  31.        if ($method === '') {  
  32.            $result = new DataReader($this);  
  33.         } else {  
  34.            if ($fetchMode === null) {  
  35.               $fetchMode = $this->fetchMode;  
  36.             }  
  37.            $result = call_user_func_array([$this->pdoStatement, $method], (array$fetchMode);  
  38.            $this->pdoStatement->closeCursor();  
  39.         }  
  40.         Yii::endProfile($token'yii\db\Command::query');  
  41.     } catch (\Exception $e) {  
  42.         Yii::endProfile($token'yii\db\Command::query');  
  43.         throw $this->db->getSchema()->convertException($e$rawSql);  
  44.     }  
  45.   
  46.     if (isset($cache$cacheKey$info)) {  
  47.         $cache->set($cacheKey, [$result], $info[1], $info[2]);  
  48.         Yii::trace('Saved query result in cache''yii\db\Command::query');  
  49.     }  
  50.     return $result;  
  51. }  
这样看来,就是讲所有的查询结果进行返回,那么会有人问不是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操作,返回第一个结果记录
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值