我是阿北,Yii学习社群正在搞技术文竞赛(获胜者送VIP权限),邀请你参加。 另外我的订阅号送书中,看详情
发布于 3个月前

小谈Yii Framework 2.0(yii2)的数据库层

这篇文章内容来自yii2团队成员cebe在2017年的一次PPT,阿北将其翻译成中文分享给大家,希望对一些这里不懂的同学可以起到抛砖引玉的功效。

【翻译参与人员】google翻译参与99% 阿北参与1%,鉴于水平有限,有的地方可能有点绕口,但是整体理解是没问题的。

关于本次内容,cebe并没有进行深度的分析,重点在于对数据层结构的梳理。

为何要引入数据库层

alt

为何yii2框架要单独做一个数据库层而不用原生的PDO等进行维护那?那岂不会更快,这里有一个很主要的原因就是代码简化和可读性,提供更加简洁并且匹配yii2风格的方法,让开发者开发思维更流畅。我想这也是框架诞生的主要目的。

另外我们独立出一层后,我们就不用重复的做数据库链接、关闭、....这样的工作,通过对数据库层有效的设计,也可以增加代码的复用。

以上的优点让我们开发变快了,那么从结构上说那?

你我都知道yii2是一个MVC框架,在这个模式中M负责数据的逻辑处理,而对于数据库应用,这些数据就存在于每一张表中,那么模型如何去处理数据那?

alt

因此我们可以做一个数据库层,就如你经常使用的Active Record,它就是模型到数据库的一种映射。

alt

还有一个很重要的原因,相对于直接使用PHP的数据库操作,我们除了使用PDO的参数绑定等方法增强数据库操作的安全性外,单独写一个数据库层也能更有效的将非法的数据挡在操作数据库之前,这无疑是框架安全方面的一个体现。

@nai8@

三层

上面说了数据库层诞生的原因,那么在yii2中,数据库层到底是如何设计的那?

alt

正如上图内容一样,你可以通过三种方式对数据库进行操作,这三种方式存在紧密的联系,且封装程度逐渐增强,你可能对cebe的ppt看不太懂,我再给大家备注下,见下图。

alt

DAO

DAO是yii2对php的PDO第一层封装,我们通过db组件的\yii\db\Connection来实现PDO与数据库的链接,接下来yii2定义了一组自己的API,比如createCommand、比如execute等来实现对数据库的操作。

相比较PDO而言,DAO的这层封装将更好的融合到yii2体系中,就如你不用执行具体的链接打开操作,只需一句话就可以了

Yii::$app->db

简单了很多,但是DAO在yii2中更多是提供给其他数据库操作方式使用,原因一是在DAO中我们仍然需要自己去写SQL语句,这样是繁琐的并且原始的。另外一个原因就是DAO返回的是数组或一个具体的值形式,很难发挥对象强大的关联性。

在这里我给大家列举一些DAO的使用方法

$conn = Yii::$app->db;
// 查询会员集合的数据
$data = $conn->createCommand("SELECT * FROM `user`")->queryAll();
// 查询一个会员
$one = $conn->createCommand("SELECT * FROM `user` WHERE xxx")->queryOne();
// 插入一个会员
$rows = $conn->createCommand()->insert(['name'=>'abei','age'=>18])->execute();
.....

当然,DAO也支持事务以及读写分离等操作。

DAO使用场景 当你使用其他接口搞不定对数据库的操作时,可以尝试使用DAO直接传入原生的SQL语句。

Query Builder

为了解决DAO必须要写入原生SQL语句的弊端,yii2封装DAO并打造出更高一级的数据库操作方式 --- Query Builder,它主要解决数据库查询需求。

提醒 Query Builder一般不会自己出来,一般都是由它的助手Query类来帮助采集数据并反馈结果。

我们终于可以不写SQL语句了,高兴,下面看看Query的使用技法。

$query = (new yii\db\Query)->select(['name','age'])->from('user');
$users = $query->all();

当然这是最简单的一个例子,但是你应该看懂了,我们总是要先实例化一个Query对象,然后使用其各种方法(比如select、from等)来接收参数构造出SQL语句,最后执行all等结果方法拿到数据。

Query 支持很多方法

  • prepare
  • batch
  • each
  • all
  • populate
  • one
  • scalar
  • column
  • count
  • sum
  • average
  • min
  • max
  • exists
  • queryScalar
  • getTablesUsedInFrom
  • select
  • addSelect
  • distinct
  • from
  • where
  • andWhere
  • orWhere
  • andFilterCompare
  • join
  • innerJoin
  • leftJoin
  • rightJoin
  • groupBy
  • addGroupBy
  • having
  • andHaving
  • orHaving
  • filterHaving
  • andFilterHaving
  • orFilterHaving
  • union
  • params
  • addParams

这些语句满足了我们对SQL语句编写的所有需求。

But~~~,Query Builder仍然返回的是一个数组,这个问题它依然没有解决。

Active Record(AR)

关于DAO和Query Builder,它们仅仅是对PDO进行了简易化处理,但是我们依然是使用了DAO直接给我们返回的结果,此刻对于yii2的开发者们,我们希望返回的东西更加对象化,因此出现了Active Record --- 活动记录。

小提醒 考虑到AR的重要性,阿北之前已经录制了一套18节的课程全面解析这种模式。传送门

大家先来看我做的这张图。

alt

类到数据库表的映射 这种映射关系是牛x的,它将数据表和数据真正的对象化了,一个user表就代表一类人,每一条数据就是一个人,表的每个列就是人的各种属性,这不就是面向对象的思维么?

这不由让我想到了MVC中的M,M不就是一个一个的抽象实体么?因此,yii2官方文档说了一句话:“Active Record就是模型”,到此刻为止Active Record这种数据库操作方式正式进入了yii2的MVC体系。

当然,上面是针对于Active Record这种模式的理念进行说明,Active Record的实现并没有说的抽象,我们看下图。

alt

AR使用Query类并且上接各种模型,将数据表的数据变成对象,我们看一个例子。

$model = User::find()->where(['id'=>1])->one();
  • User是一个模型类,继承于AR
  • User::find()返回的是Query对象并执行where(['id'=>1])这些查询操作。
  • one()返回了一个User的对象,里面含有各种User属性及值及等等。

记住one()返回的是一个对象了,因此我们可以进行如下操作

echo $model->name;
echo $model->age;

对象的好处你会在AR关联方法上清晰的看到。

关于AR的方法,我们可以在ActiveRecordActiveRecordInterface中找到。

再说一遍,AR的意义在于我们将一条条记录转化为一个个活生生的对象了,此刻user表中的一条记录已经是一个活生生的人了。

AR对象的强大继续体现 --- 关联

现在你的数据库中是每一个类和对象了,我现在想找到张三同学的所有订单,请用面向对象的思维去思考,它是不是如下的伪代码形式

张三->订单s

但是我们都知道,张三在user表中而订单也是独立的表,怎么办?AR能帮我们实现这样的需求,试想一下这样之后你的代码将有多么简洁、多么的容易理解和阅读,太tmd的语义化了。

yii2的AR提供了一组关联方法,能将表之间的关系也映射给对象关系,比如张三的事情我们可以如下做

// User表
Class User extends yii\db\ActiveRecord {
  public function getOrders(){
    return $this->hasMany(Order::className(),['user_id'=>'id']);
  }
  ....
}

两个独立的表,通过hasMany进行了关联,于是张三的订单,你可以这样写了

$user = User::find()->where("找到张三")->one();
$orders = $user->orders

简单了么?看明白了么?

除了hasMany还有一个重要的关联,就是这个订单是谁的那?

// User表
Class Order extends yii\db\ActiveRecord {
  public function getUser(){
    return $this->hasOne(User::className(),['id'=>'user_id']);
  }
  ....
}

hasOne 实现了这个需求,你一般会这样用

$order = Order::findOne(9527);
echo $order->user->name;// 张三

关联带来的意义是非凡的,行云流水般的写代码。当然关联也会带来一些性能上的损失,不过我们可以通过优化来改善它,这远小于AR关联带来的好处。

关联是一门比较大的课程,不单单是语法上的,更多是你对数据库中每个表的理解。当然你可能不知道关联也有一些复杂的写法,比如

  public function getUser(){
    return $this->hasOne(User::className(),['id'=>'user_id'])->where()->limit()->orderBy();
  }

是的,你可以加很多限制条件。

你可能在思考,为什么都是查询?那我们就说说AR的其他操作。 既然是映射就不单单是获取,还有对象的新建、更新和删除,这些数据库操作也都映射到了AR上。并且这些操作更好理解了。

比如我现在要修改用户的名字

// 1 找到这个人
$user = User::findOne(9527);
// 2 将这个人的名字改变为李四
$user->name = "李四";
// 3 开始更新
$user->update();

我们只需要关注业务和AR,数据库我们可以不知道。

比如我现在要增加一个人

// 1 新建一个人
$user = new User();
// 2 这个人叫小吴
$user->name = "小吴";
// 3 开始增加
$user->insert();

对于更新和插入,我们可以使用同一的方法save,具体yii2会自行判断。

删除一个人

// 1 找到这个人
$user = User::findOne(9527);
// 2 删除它
$user->delete()

我的感悟 我们一直在操作着数据库,但是我们从不知道它的存在,在AR的帮助下,我们只需要关注业务中的每一个对象及其关系。

好处还很多 AR还带来了很多好处,比如和ActiveDataProvider合作轻松渲染表格,比如和ActiveFrom合作轻松验证信息并提交数据,这有待于大家更好的挖掘。

小结

以上是cebe对数据库层的一个框架说明,当然他只是给了ppt,具体每个页面的内容是我按照自己理解加上的,难免有不尽人意的地方,望各位兄弟不要介意。

o( ̄▽ ̄)d 做点赞第一人吧
没有找到数据。

作者 收到的礼物

  • 暂时没有礼物
-
在线