YII2框架查询源码解析

来源:互联网 发布:淘宝上怎么买av的资源 编辑:程序博客网 时间:2024/06/09 21:32

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

[php] view plain copy 在CODE上查看代码片派生到我的代码片
  1. return static::findOne(['id' => $id'status' => self::STATUS_ACTIVE]);  

findOne定义是:
[php] view plain copy 在CODE上查看代码片派生到我的代码片
  1. public static function findOne($condition)  
  2. {  
  3.      return static::findByCondition($condition)->one();  
  4. }  

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

[php] view plain copy 在CODE上查看代码片派生到我的代码片
  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类中定义的
[php] view plain copy 在CODE上查看代码片派生到我的代码片
  1. public static function find()  
  2. {       
  3.      return Yii::createObject(ActiveQuery::className(), [get_called_class()]);  
  4. }  

也就是说$query是一个ActiveQuery的对象,其需要传入的参数是需要进行查询的类的名字"User";
中间这一部分,先不要看,因为还没时间看,直接看下面的一行addWhere该函数的定义是在Query类中
[php] view plain copy 在CODE上查看代码片派生到我的代码片
  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类当中

[php] view plain copy 在CODE上查看代码片派生到我的代码片
  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函数,该函数定义如下:

[php] view plain copy 在CODE上查看代码片派生到我的代码片
  1. public function one($db = null)  
  2. {  
  3.     return $this->createCommand($db)->queryOne();  
  4. }  

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

[php] view plain copy 在CODE上查看代码片派生到我的代码片
  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语句

[php] view plain copy 在CODE上查看代码片派生到我的代码片
  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
[php] view plain copy 在CODE上查看代码片派生到我的代码片
  1. return $db->createCommand($sql$params);  

[php] view plain copy 在CODE上查看代码片派生到我的代码片
  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函数如下:
[php] view plain copy 在CODE上查看代码片派生到我的代码片
  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函数
[php] view plain copy 在CODE上查看代码片派生到我的代码片
  1. public function queryOne($fetchMode = null)  
  2. {  
  3.     return $this->queryInternal('fetch'$fetchMode);  
  4. }  
queryInternal函数定义

[php] view plain copy 在CODE上查看代码片派生到我的代码片
  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操作,返回第一个结果记录
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 手机触摸屏不灵了怎么办 mac触摸板失灵怎么办 苹果屏幕触摸失灵怎么办 三星手机屏幕没反应怎么办 手机开机定屏怎么办 手机触摸局部失灵怎么办 苹果手机屏幕按键失灵怎么办 5s锁屏键坏了怎么办 平板版本太低怎么办 手机屏局部失灵怎么办 iphone8触屏不灵敏怎么办 苹果机8屏幕失灵怎么办 车钥匙丢车上怎么办 指纹锁华盖坏了怎么办 非法入了户口怎么办 司考成绩单丢了怎么办 小饭桌转让手续怎么办 两个领导不和你怎么办 两个领导意见不一致怎么办 两个领导对立我怎么办 投诉申通没用怎么办 领导作风有问题怎么办 做完火疗受风了难受怎么办 鼻子做的不好看怎么办 埋线双眼皮出血怎么办 割完双眼皮出血怎么办 全切双眼皮出血怎么办 割双眼皮出血了怎么办 割双眼皮后出血怎么办 双眼皮手术后出血怎么办 缝双眼皮开了怎么办 朋友网没了怎么办 压疮发生后怎么办 学籍账号已登录怎么办 护士学分不达标怎么办 高中学籍错了怎么办 定时器时间太短不够用怎么办? 考试时间不够用怎么办 感觉时间不够用怎么办 学习时间不够用怎么办 天天时间不够用怎么办