Yii2框架是一个非常庞大但是并不臃肿的 php 框架
使用Yii2 框架,可以极大的提升开发效率

1.Yii 框架基础

此框架的三个基础概念

  1. 属性
  2. 事件
  3. 行为

    1.1属性

    一看到这个词可能很迷惑,那么什么是属性呢
    比如说,你现在正在玩一款角色扮演的游戏,角色双手分别有一把 十方大剑,一个人十方盾牌。那么从面向对象的角度而言,你的角色类需要有 左手武器和右手武器两个成员变量。并且角色类可以从 左手武器 和 右手武器中获得力量属性

1.2事件

还是以角色扮演游戏为例,游戏里面都有任务系统,而任务会分为主线任务和支线任务,在做主线任务时通常会触发一系列的支线任务。这些支线任务的触发就可以认为是一系列的事件

1.3行为

框架的行为也可以使用角色扮演游戏来举例,你在 无尽荒原 捡到了一本魔法书,学会了禁咒魔法:召唤神龙。
于是你把程序一改,给你的类加一个召唤魔法的方法,这是不可能的。我们必须让你的类拥有一个动态添加方法的功能,于是所有拿到魔法书的人或者限定职业的人,都能学会这个禁咒,这就是 行为 的作用,动态给类增加方法。

2.设计模式

2.1依赖注入

框架采用了现在市面上最常见的一种模式,MVC 模式
MVC 只是一种大框架上的设计模式,其核心思想是分层,最终目的是解耦。框架在 MVC 的基础上,应用了很多经典的设计模式以及后来发展的设计模式
其中最重要的就是:

  1. 依赖注入
  2. 服务定位器

那么什么是依赖注入呢
先来一段没有依赖注入的代码:

  1. <?php
  2. // 这段代码将 db1 中的t1表的数据备份到 db2 库的 t2 中。
  3. // 所使用的变量都在逻辑过程中申请。
  4. class Archive {
  5. public function doArchive() {
  6. $dataDB = new DB1();
  7. $data = $dataDB->query("select * from t1");
  8. $backDB = new DB2();
  9. foreach ($data as $key => $value) {
  10. $backDB->query("insert into t2 values ". implode(',', $value));
  11. }
  12. die("备份完成");
  13. }
  14. }

那么如果再来一个需求,让你把db3 的数据备份到 db4 中,逻辑相同,那么你该怎么办呢?
有道友就说了,那还不简单,copy& paste,搞定
于是,你多了一个方法或者一个类。
一直到你的方法和类增加到10个以上时,你都不会有什么感觉。
知道产品说现在需求变了,让你把所有的备份库都改成db3,那么,你就要把手头上的工作都停下来,然后将代码中的备份库全改成 db3。
而且,如果你使用的是静态语言,那么,你就要把代码再重新编译一遍。
那么就有了下面这种:

  1. class ArchiveNew {
  2. private $originDb;
  3. private $backDb;
  4. public function doArchive() {
  5. $dataDB = new $this->originDb();
  6. $data = $dataDB->query("select * from t1");
  7. $backDB = new $this->backDb();
  8. foreach ($data as $key => $value) {
  9. $backDB->query("insert into t2 values ". implode(',', $value));
  10. }
  11. die("备份完成");
  12. }
  13. /**
  14. * @param mixed $originDb
  15. */
  16. public function setOriginDb($originDb)
  17. {
  18. $this->originDb = $originDb;
  19. }
  20. /**
  21. * @param mixed $backDb
  22. */
  23. public function setBackDb($backDb)
  24. {
  25. $this->backDb = $backDb;
  26. }
  27. }

这个方法,把所有的需要用到的数据库都放到了外面来进行管理,那么我们称这几个数据为依赖,称在外部设置数据库的行为为依赖注入。
这只是一种很原始的使用方式,你可以继续延伸,将外部依赖放到统一的地方去管理,那么就有了注入容器(di Container)

2.2服务定位器

服务定位器就像是一个注册中心,向服务定位器中注册一个a服务,可以使用a这个名字从服务定位器中取出这个服务

  1. $locator = new ServiceLocator;
  2. $locator->set('a');
  3. $locator->get('a');

服务定位器是基于依赖注入的,在获取服务时,其实会在容器中先注册一个服务。

3.请求与响应

这一节其实主要讲的就是请求
请求的内容会比较多,还涉及到网络协议等知识
列举一下重要的几点:

  1. 路由美化(将原始的路由修改成比较美观的地址)
  2. url 解析器(将美化过的路由解析成原始请求)
  3. 请求管理(包括请求头部,请求体,解析器等)

4.数据库

Yii2 框架整体结构 - 图1

4.1类型转换

框架为了兼容各种数据库,对数据库类型做了多层封装,并作了一系列的转换规则:
Yii2 框架整体结构 - 图2

4.2事务

框架支持事务嵌套,但是嵌套的事务必须成对出现(注意!!)
看到事务这一节的时候,正巧同事出了一个bug,在脚本的循环中,出错后没有commit 或者 rollback ,导致,接下来的生成的事务都成了这个事务的子事务。而框架的嵌套事务,实际上是使用代码模拟的,如果父事务没有提及,那么子事务永远不会提交。

4.3事件

  1. const EVENT_INIT = 'init'; // 初始化对象时触发
  2. const EVENT_AFTER_FIND = 'afterFind'; // 执行查询结束时触发
  3. const EVENT_BEFORE_INSERT = 'beforeInsert'; // 插入结束时触发
  4. const EVENT_AFTER_INSERT = 'afterInsert'; // 插入之前触发
  5. const EVENT_BEFORE_UPDATE = 'beforeUpdate'; // 更新记录前触发
  6. const EVENT_AFTER_UPDATE = 'afterUpdate'; // 更新记录后触发
  7. const EVENT_BEFORE_DELETE = 'beforeDelete'; // 删除记录前触发
  8. const EVENT_AFTER_DELETE = 'afterDelete'; // 删除记录后触发

4.4乐观锁与悲观锁

框架自带了乐观锁的实现,如果有类型需求,可以在重载yii\db\ActiveRecord::optimisticLock() 方法,返回数据库中的版本号字段即可。在更新与删除时,框架会做相应的操作,来保证更新的数据是自己拿到的数据,而不是被别人给修改过了的。

5.总结

Yii2 框架整体结构 - 图3