文档章节

Yii2学习之DAO和Query

botkenni
 botkenni
发布于 2017/06/07 06:59
字数 1517
阅读 15
收藏 0

简单介绍

上节介绍了AR类,是以面向对象的方式操作数据库,yii2还提供了一套统一的数据库访问层API(Data Access Objects),可以以SQL语句方式操作数据库表,能完成AR类的任何操作,但是相对与AR使用DAO需要写大量的sql语句,如果你的sql语句很复杂,会在后续工作中很难维护,所以一般建议还是使用AR类。但是不管DAO还是AR他们都是通过Query基类创建sql语句(AR类通过创建AQ对象操作),最后都是通过Command类的queryInternal方法的PDO操作返回查询的数据。

源码解析

基本的DAO操作
$query = Yii::$app->db->createCommand(“select * from oc_user where id= :id", [':id' => $id])->queryOne();

Query查询返回数组

$query = (new Query())
    ->select('*')
    ->from('oc_user')
    ->where('id = :id', [':id' => $id])
    ->one();

 

上面两种格式返回oc_user中id为指定id的行。返回类型为数组|布尔型。Yii::app->db 获取到连接实例,有了连接实例就可以通过createCommand方法创建一条查询命令行,这时还没有执行查询语句,真正执行查询是在one()方法中。

 public function createCommand($sql = null, $params = [])
 {
     $command = new Command([
         'db' => $this,
         'sql' => $sql,
     ]);
     return $command->bindValues($params);
 }

 

createCommand只是创建了个Command类,将db连接实例对象和sql查询语句保存到对应的属性中,返回创建的Command类对象。

public $db; 
private $_sql;

 

然后调用Command类的queryOne方法调用到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);//该方法中执行$pdo->prepare($sql);

      $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;
  }

 

真正执行查询语句是在$this->pdoStatement->execute();后,queryOne方法的$method是fetch,固在call_user_func_array中调用PDO.PHP的fetch方法采集查询的数据,最后以数组形式返回。

Query的查询与createCommand类似,只不过中间多了步QueryBuilder的解析,解析每一个方法中字段的值组合成sql语句,最后还是调用Command对象的queryOne方法去查询。

public function select($columns, $option = null)
{
    if (!is_array($columns)) {
        $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
    }
    $this->select = $columns;
    $this->selectOption = $option;
    return $this;
}

 

public function where($condition, $params = [])
{
    $this->where = $condition;
    $this->addParams($params);
    return $this;
}

 

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

 

public function createCommand($db = null)
{
    if ($db === null) {
        $db = Yii::$app->getDb();
    }
    list ($sql, $params) = $db->getQueryBuilder()->build($this);

    return $db->createCommand($sql, $params);
}

Query中的每个操作方法都是返回$this对象本身(select,from,where等等),在one方法时创建了QueryBuiler类通过build方法解析Query对象的数据,然后执行db连接对象的createCommand将sql语句和db对象复制给Command对象,然后调用Command对象的queryOne执行查询sql语句返回结果,后面流程同上。

常用操作

//返回id为1对应的行
$user = Yii::$app->db->createCommand('SELECT * FROM oc_user WHERE id=1')->queryOne();

//返回oc_user表的所有数据
$user = Yii::$app->db->createCommand('SELECT * FROM oc_user')->queryAll();

//返回oc_user表的name列
$user = Yii::$app->db->createCommand('SELECT name FROM oc_user')->queryColumn();

//返回id为1对应行的第一列的值
$user = Yii::$app->db->createCommand('SELECT * FROM oc_user WHERE id=1')->queryScalar();

//删除id为1的一行1
Yii::$app->db->createCommand()->delete('oc_user', 'id = 1')->execute();

//引用变量时不要直接写到sql语句中,安全性
$user = Yii::$app->db->createCommand("SELECT * FROM oc_user WHERE id=$id")->queryOne();
不安全,如果别人攻击你服务器,$id传入一个delete sql语句很有可能会把你的数据表删除掉
$user = Yii::$app->db->createCommand("SELECT * FROM oc_user WHERE id=:id", [':id' => $id])->queryOne();
改成这种格式,createCommand会以bindValues绑定参数,在调用PDO操作时是会从_pendingParams读取相应的值以bindValue操作查询。(这个要看PDO.php的bindValue这个处理原理,应该会去做检测,有时间可以研究下)

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;
}

//事物操作,执行多个数据操作时,封装到一个事物中可以保证数据的一致性,如果中间操作失败可以回滚到操作之前的位置,防止数据出现一个保存,一个失败等问题。

$transaction = $connection->beginTransaction();
try {
    $connection->createCommand($sql1)->execute();
     $connection->createCommand($sql2)->execute();
    // ... 执行其他 SQL 语句 ...
    $transaction->commit();
} catch(Exception $e) {
    $transaction->rollBack();
}

Query高级查询

query的where方法支持多种格式(1、纯字符串 2、哈希格式 3、操作符格式)
//字符串
->Where('state = 1')
//哈希格式
->Where(['state' => 1])
//操作符格式
->Where(['in', 'state', [1,2,3]])

//常用操作符格式

->where(['between', 'state', 1, 10])
->where(['and', 'id=1', 'type=1'])
->filterWhere(['like', 'name', 'tester'])
->andWhere(['>', 'age', 10])

//order by id ASC
->orderBy('id ASC')

//group by 分组查询
->groupBy(['id', 'status']);

//过滤
->having(['status' => 1]);

//从第20条开始读取10条数据
->limit(10)->offset(20);

//从第10条开始读取20条数据
->limit('10, 20')

//联合查找
->leftJoin('post', 'post.user_id = user.id');

//union组合

$query1 = (new \yii\db\Query())
    ->select("id, category_id AS type, name")
    ->from('post')
    ->limit(10);

$query2 = (new \yii\db\Query())
    ->select('id, type, name')
    ->from('user')
    ->limit(10);

$query1->union($query2);

最后调用->one(); ->all(); 执行查询操作返回需要的数据。

//批量处理,一次获取一条查询
foreach ($query->each() as $user)

例子解析:

$subQuery = (new Query())
    ->select('*')
    ->from('oc_task_state_log')
    ->where(['in', 'from_state', [OcButlerTask::BUTLER_TASK_ASSIGN, OcButlerTask::BUTLER_TASK_CONTACT, OcButlerTask::BUTLER_TASK_UNSUCCESS_POTENTIAL, OcButlerTask::BUTLER_TASK_CHANGE_TIME]])
    ->andWhere(['in', 'to_state', [OcButlerTask::BUTLER_TASK_EARNEST_MONEY, OcButlerTask::BUTLER_TASK_DIRECT]])
    ->andWhere(['between', 'create_at', $start_time, $end_time]);

$query = (new Query())
    ->select('a.create_at, a.task_id, oc_butler.nick_name as nick_name')
    ->from(['a' => $subQuery])
    ->leftJoin('oc_butler_task', 'a.task_id = oc_butler_task.id')
    ->leftJoin('oc_earnest_money', 'oc_earnest_money.id = oc_butler_task.earnest_money_id')
    ->leftJoin('oc_butler', 'oc_butler.id = oc_butler_task.butler_id');

$query->andWhere('oc_butler.nick_name = :nick_name', [':nick_name' => $params['nick_name']]);
$query->orderBy('create_at DESC');

foreach($query->each(10) as $task_log){
    //...
}

本文转载自:http://blog.csdn.net/xiaog351/article/details/47729529

botkenni
粉丝 20
博文 410
码字总数 435359
作品 0
西城
程序员
私信 提问
yii2 new Expression

yii2 new Expression,new Expression,new yiidbExpression,yii2 FINDINSET,yii sql,yiidbExpression 演示一段yii2 关于 new Expression 或者 new yiidbExpression 的sql语句 首先 use yiidbE......

dragon_tech
07/17
15
0
当Yii遇上不支持pdo_mysql的服务器

(2014-10-9,在使用过程中仍发现不少问题,已迁移至https://github.com/xiilei/php-functions/tree/master/yii,不定期维护,下列代码不再更新) 这真是一件很郁闷的事情,项目的一个子项目(cm...

xilei
2014/03/28
2K
2
YII访问数据库(Yii Dao)

CDbConnection: 一个抽象数据库连接 CDbCommand: SQL statement CDbDataReader: 匹配结果集的一行记录 CDbTransaction:数据库事务 访问数据库前需要建立数据库连接;使用DAO建立一个抽象数据...

rooney
2014/08/15
240
0
详解yii2实现分库分表的方案与思路

前言 大家可以从任何一个gii生成model类开始代码上溯,会发现:yii2的model层基于ActiveRecord实现DAO访问数据库的能力。 而ActiveRecord的继承链可以继续上溯,最终会发现model其实是一个c...

dragon_tech
07/15
7
0
Yii CDbCriteria 常用方法

Yii CDbCriteria 常用方法 注:$c = new CDbCriteria();是ActiveRecord的一种写法,使ActiveRecord更加灵活,而不是手册中DAO(PDO)和Query Builder。 这是Yii CDbCriteria的一些笔记和常用...

Neo_
2013/10/03
121
0

没有更多内容

加载失败,请刷新页面

加载更多

面向对象编程

1、类和对象 类是对象的蓝图和模板,而对象是实例;即对象是具体的实例,类是一个抽象的模板 当我们把一大堆拥有共同特征的对象的静态特征(属性)和动态特征(行为)都抽取出来后,就可以定...

huijue
今天
8
0
redis异常解决 :idea启动本地redis出现 jedis.exceptions.JedisDataException: NOAUTH Authentication required

第一次安装在本地redis服务,试试跑项目,结果却出现nested exception is redis.clients.jedis.exceptions.JedisDataException: NOAUTH Authentication required错误,真是让人头疼 先检查一...

青慕
今天
12
0
Spring 之 IoC 源码分析 (基于注解方式)

一、 IoC 理论 IoC 全称为 Inversion of Control,翻译为 “控制反转”,它还有一个别名为 DI(Dependency Injection),即依赖注入。 二、IoC方式 Spring为IoC提供了2种方式,一种是基于xml...

星爵22
今天
25
0
Docker安装PostgresSql

Docker安装PostgresSql 拉取docker镜像 # docker pull postgres:10.1010.10: Pulling from library/postgres9fc222b64b0a: Pull complete 38296355136d: Pull complete 2809e135bbdb: Pu......

Tree
今天
8
0
内容垂直居中

方法一: 采用上下 padding 形式,将内容放置在垂直居中 .line { padding: 2% 0; text-align: center; height: 5px;} <div class="line"> 内容垂直居中</div> 方法二: 采......

低至一折起
今天
20
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部