yii2项目实战-那些年错过的数据库查询

对于框架而言,与数据库交互的操作自然少不得。yii2也提供了下面几种与数据库交互的方式,先来简单了解下

1、简单粗暴的原生sql查询

2、程序化、不需要关心DBMS的sql查询

3、AR提供了数据库与模型之间的交互

关于上面三种方式,手册上介绍的都很详细,今天我们拿前面两条介绍,给划划重点,避免一些小坑。

数据库访问对象(Database Access Objects)DAO

DAO便是我们首先要介绍的,如果不依赖框架,相信大部分人都会选择使用PDO与数据库进行交互,但是写起来稍微麻烦些。DAO是什么呢?写到这里,相信一部分人都能猜到了。DAO是构建与PDO之上,让我们可以用更简单高效方便的方式进行数据库操作。

吐槽:老师你能不能说人话?

简单的说,就是在yii框架的基础之上执行原生的sql语句。

来看看如何实现:

第一步自然是进行数据库配置了,相信各位都不陌生,在刚开始学习yii2的时候都知道怎么配置了,不懂的请先移步这里

下一步我们创建 yii\db\Command 对象就可以执行sql语句了

1
$result = Yii::$app->db->createCommand($sql)->查询方法

常见的’查询方法’都有这些

1
queryAll、queryOne、queryScalar

例如,如果我们想获取article表内所有的数据(当然我们很少这样做)

1
2
$sql = "SELECT * FROM `article`";
$result = Yii::$app->db->createCommand($sql)->queryAll();

同理,queryOne()是获取一条数据,queryScalar是获取一个值

简单吧,别着急,我们来看两个小小的坑

1、queryOne方法只返回结果集中的一条数据,实际查询的条件语句中并不会带上limit 1,如果你调用该方法查询数据,但是不能保证返回的结果集只有一条,为了性能考虑,请务必添加limit 1这个条件

2、在一些接收用户提交的参数的where条件上,千万不要像下面这样直接拼接where条件

1
2
3
$id = $_GET['id'];
$sql = "SELECT * FROM `article` WHERE id = '{$id}'";
$result = Yii::$app->db->createCommand($sql)->queryAll();

实际开发中,为了避免sql注入,请一定不要参考上面的写法!绑定参数是一种避免sql注入的友好方式,来看下正确的写法

1
2
3
$result = Yii::$app->db->createCommand('SELECT * FROM `article` WHERE id=:id')
->bindValue(':id', $_GET['id'])
->queryOne();

如果where条件的参数不是来自于用户端,那随便写。

查询构建器(Query Builder)

原生sql的写法很简单,但是危险性也很大,一个不小心可能就被sql注入了,这是非常危险、实际开发中一定不能发生的事情。相对于原生sql,query builder就安全的多了,而且,一旦你的sql很长,query builder构建的sql语句,可读性也会更强。这么好用的工具,快来试试吧。

步骤很简单,你只需要实例化\yii\db\Query类,然后调用查询方法即可。

1
2
3
4
5
6
7
$query = new \yii\db\Query;
$results = $query
->select(['id', 'email'])
->from('user')
->where(['username' => 'aa'])
->limit(5)
->all();

有些人可能很疑惑,我并不知道有你上面那些方法呀?这个不难,你只需要打开\yii\db\Query类,多看上几眼该类的方法都有哪些,下回就懂如何调用了。

我们接着说上面构建的一个用法,可能很多同学要问,其执行的具体sql又是怎么样的呢?

有两种方式:

1、借助yii-debug的使用,在DB一栏可观察到具体执行的sql语句

2、在上面的基础上,执行下面的sql语句,打印具体的sql语句

1
2
$sql = $query->createCommand()->getRawSql();
var_dump($sql);

打印的结果如下

1
string(62) "SELECT `id`, `email` FROM `user` WHERE `username`='aa' LIMIT 5"

当然啦,实际中也建议各位会使用debug来分析,省时省事嘛。

来分析下另外一个问题,如果上面的例子中username对应的值aa是从用户端接收过来的,请问下这里用不用绑定参数呢?答案是没必要的,我们来看下实际构建的sql语句

1
2
3
4
SELECT `id`, `email` FROM `user` WHERE `username`=:qp0 LIMIT 5

//通过 $query->createCommand()->getSql(); 可得
即username已经是被绑定的。

来看下常见的几个查询方法,有all那自然也有对应的one、scalar啦,当然,还有count、sum、min、average等等,这就需要各位多多去挖掘啦。

需要提醒的是,one方法同样跟我们上面所说的一样,没有limit 1,在不保证结果集是一条的基础上,请一定要在query上追加limit(1)。

上面我们单纯的介绍了简单的使用方法,让我们来看看一些更为复杂的操作。

假设我们要构建的sql如下,来看看query如何构建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1、SELECT `id`, `email` FROM `user` WHERE `id` IN (1,2,3)
2、SELECT `id`, `email` FROM `user` WHERE `id` = 1 OR `id` = 2
3、SELECT `id`, `email` FROM `user` WHERE `username` LIKE '%aa%'
4、SELECT `id`, `email` FROM `user` WHERE (`username` LIKE '%aa%') OR (`title` LIKE '%aa%')
5、SELECT `id`, `email` FROM `user` WHERE 1 = 1 AND (`name` = 'zhangsan' OR `age` = 20) //注意小括号的位置,在有些情况下错不得
query构建结果分别如下

$query = new \yii\db\Query;

1、$results = $query
->select(['id', 'email'])
->from('user')
->where(['IN', 'id', [1,2,3]])
->all();
2、$results = $query
->select(['id', 'email'])
->from('user')
->where(['OR', 'id=1', 'id=2'])
->all();
3、$results = $query
->select(['id', 'email'])
->from('user')
->where(['LIKE', 'username', 'aa'])
->all();
4、$results = $query
->select(['id', 'email'])
->from('user')
->where(['LIKE', 'username', 'aa'])
->orWhere(['LIKE', 'title', 'aa'])
->all();

或者:

1
2
3
4
5
6
7
8
9
10
11
$results = $query
->select(['id', 'email'])
->from('user')
->where(['OR', ['LIKE', 'username', 'aa'], ['LIKE', 'title', 'aa']])
->all();
5、$results = $query
->select(['id', 'email'])
->from('user')
->where([1 => 1])
->andWhere(['OR', 'name=zhangsan', 'age=20'])
->all();

下面我们再来看一下连表的操作

1
2
3
4
5
SELECT `t`.`id`, `t`.`email`, `t2`.`type` 
FROM `user` AS `t`
LEFT JOIN `auth` AS `t2`
ON `t`.`id` = `t2`.`id`
WHERE `t`.`id` = 1

query的实现方式

1
2
3
4
5
6
7
$query = new \yii\db\Query;
$results = $query
->select(['t.id', 't.email', 't2.type'])
->from(['t' => 'user'])
->leftJoin(['t2' => 'auth', 't.id' => 't2.id'])
->where(['t.id' => 1])
->all();

除此之外,还有很多诸如orderBy、groupBy、having、union等方法我们就不一一列举了