深入理解Yii2
导读
Yii 是什么
Yii2.0 的亮点
背景知识
如何阅读本书
Yii基础
属性(Property)
事件(Event)
行为(Behavior)
Yii约定
Yii应用的目录结构和入口脚本
别名(Alias)
Yii的类自动加载机制
环境和配置文件
配置项(Configuration)
Yii模式
MVC
依赖注入和依赖注入容器
服务定位器(Service Locator)
请求与响应(TBD)
路由(Route)
Url管理
请求(Reqeust)
Web应用Request
Yii与数据库(TBD)
数据类型
事务(Transaction)
AcitveReocrd事件和关联操作
乐观锁与悲观锁
附录
附录1:Yii2.0 对比 Yii1.1 的重大改进
附录2:Yii的安装
本文档使用 MrDoc 发布
-
+
首页
附录1:Yii2.0 对比 Yii1.1 的重大改进
> 这部分内容是专门为已经有Yii1.1基础的读者朋友写的。将Yii2.0与Yii1.1的不同点着重写出来,对比学起来会快得多。 而对于从未接触过Yii的读者朋友,这部分内容扫一扫就可以了,作为对过往历史的一个了解就够了。 如果有的内容你一时没看明白,也不要紧,本书的正文部分会讲清楚的。 另外,没有Yii1.1的经验,并不妨碍对Yii2.0的学习。 > Yii官方有专门的文档归纳总结1.1版本和2.0版本的不同。以下内容,主要来自于官方的文档,我做了下精简, 选择比较重要的变化,并加入了一些个人的经验。 ### PHP新特性 > 从对PHP新特性的使用上,两者就存在很大不同。Yii2.0大量使用了PHP的新特性,这在Yii1.1中是没有的。因此,Yii2.0对于PHP的版本要求更高,要求PHP5.4及以上。Yii2.0中使用到的PHP新特性,主要有: * 命名空间(Namespace) * 匿名函数 * 数组短语法形式: [1,2,3] 取代 array(1,2,3) 。这在多维数组、嵌套数组中,代码更清晰、简短。 * 在视图文件中使用PHP的 标签,取代 echo 语句。 * 标准PHP库(SPL) 类和接口,具体可以查看 SPL Class and Interface * 延迟静态绑定, 具体可以查看 Late Static Bindings * PHP标准日期时间 * 特性(Traits) * 使用PHP intl 扩展实现国际化支持, 具体可以查看 PECL init 。 > 了解Yii2.0使用了PHP的新特性,可以避免开发时由于环境不当,特别是开发生产环境切换时,产生莫名其妙的错误。 同时,也是让读者朋友借机学习PHP新知识的意思。 ### 命名空间(Namespace) > Yii2.0与Yii1.1之间最显著的不同是对于PHP命名空间的使用。Yii1.1中没有命名空间一说, 为避免Yii核心类与用户自定义类的命名冲突,所有的Yii核心类的命名,均冠以 C 前缀,以示区别。 > 而Yii2.0中所有核心类都使用了命名空间,因此, C 前缀也就人老珠黄,退出历史舞台了。 > 命名空间与实际路径相关联,比如 yii\\base\\Object 对应Yii目录下的 base/Object.php 文件。 ### 基础类 > Yii1.1中使用了一个基础类 CComponent ,提供了属性支持等基本功能,因此几乎所有的Yii核心类都派生自该类。 到了Yii2.0,将一家独大的 CComponent 进行了拆分。拆分成了 yii\\base\\Object 和yii\\base\\Component 。 拆分的考虑主要是 CComponent 尾大不掉,有影响性能之嫌。 于是,Yii2.0中,把 yii\\base\\Object 定位于只需要属性支持,无需事件、行为。 而 yii\\base\\Component 则在前者的基础上,加入对于事件和行为的支持。 这样,开发者可以根据需要,选择继承自哪基础类。 > 这一功能上的明确划分,带来了效率上的提升。在仅表示基础数据结构,而非反映客观事物的情况下, yii\\base\\Object 比较适用。 > 值得一提的是, yii\\base\\Object 与 yii\\base\\Component 两者并不是同一层级的,前者是后者他爹。 ### 事件(Event) > 在Yii1.1中,通过一个 on 前缀的方法来创建事件,比如 CActiveRecord 中的 onBeforeSave() 。 在Yii2.0中,可以任意定义事件的名称,并自己触发它: ``` $event = new \\yii\\base\\Event; // 使用 trigger() 触发事件 $component->trigger($eventName, $event); // 使用 on() 前事件handler与对象绑定 $component->on($eventName, $handler); // 使用 off() 解除绑定 $component->off($eventName, $handler); ``` ### 别名(Alias) > Yii2.0中改变了Yii1.1中别名的使用形式,并扩大了别名的范畴。 Yii1.1中,别名以 . 的形式使用: ``` RootAlias.path.to.target ``` > 而在Yii2.0中,别名以 @ 前缀的方式使用: `@yii/jui` > 另外,Yii2.0中,不仅有路径别名,还有URL别名: ``` // 路径别名 Yii::setAlias('@foo', '/path/to/foo'); // URL别名 Yii::setAlias('@bar', 'http://www.example.com'); ``` > 别名与命名空间是紧密相关的,Yii建议为所有根命名空间都定义一个别名,比如上面提到的yii\\base\\Object , 事实上是定义了 @yii 的别名,表示Yii在系统中的安装路径。 这样一来,Yii就能根据命名空间找到实际的类文件所在路径,并自动加载。这一点上,Yii2.0与Yii1.1并没有本质区别。 > 而如果没有为根命名空间定义别名,则需要进行额外的配置。将命名空间与实际路径的映射关系,告知Yii。 > 关于别名的更详细内容请看 _别名(Alias)_ 。 ### 视图(View) > Yii1.1中,MVC(model-view-controller)中的视图一直是依赖于Controller的,并非是真正意义上独立的View。 Yii2.0引入了 yii\\web\\View 类,使得View完全独立。这也是一个相当重要变化。 > 首先,Yii2.0中,View作为Application的一个组件,可以在全局中代码中进行访问。 因此,视图渲染代码不必再局限于Controller中或Widget中。 > 其次,Yii1.1中视图文件中的 $this 指的是当前控制器,而在 Yii2.0中,指的是视图本身。 要在视图中访问控制器,可以使用 $this->context 。这个 $this->context 是指谁调用了yii\\base\\View::renderFile() 来渲染这个视图。 一般是某个控制器,也可以是其他实现了yii\\base\\ViewContextInterface 接口的对象。 > 同时,Yii1.1中的 CClientScript 也被淘汰了,相关的前端资源及其依赖关系的管理,交由Assert Bundle yii\\web\\AssertBundle 来专职处理。 一个Assert Bundle代表一系列的前端资源,这些前端资源以目录形式进行管理,这样显得更有序。 更为重要的是,Yii1.1中需要你格外注意资源在HTML中的顺序,比如CSS文件的顺序(后面的会覆盖前面的), JavaScript文件的顺序(前后顺序出错会导致有的库未加载)等。 而在Yii2.0中,使用一个Assert Bundle可以定义依赖于另外的一个或多个Assert Bundle的关系, 这样在向HTML页面注册这些CSS或者JavaScript时,Yii2.0会自动把所依赖的文件先注册上。 > 在视图模版引擎方面,Yii2.0仍然使用PHP作为主要的模版语言。 同时官方提供了两个扩展以支持当前两大主流PHP模版引擎:Smarty和Twig,而对于Pardo引擎官方不再提供支持了。 当然,开发者可以通过设置 yii\\web\\View::$renderers 来使用其他模版。 > 另外,Yii1.1中,调用 $this->render(viewFile, ...) 是不需要使用 echo 命令的。 而Yii2.0中,记得 render() 只是返回视图渲染结果,并不对直接显示出来,需要自己调用 echo ``` echo $this->render('_item', ['item' => $item]); ``` > 如果有一天你发现怎么Yii输出了个空白页给你,就要注意是不是忘记使用 echo 了。 还别说,这个错误很常见,特别是在对Ajax请求作出响应时,会更难发现这一错误。请你们编程时留意。 > 在视图的主题(Theme)化方面,Yii2.0的运作机理采用了完全不同的方式。 在Yii2.0中,使用路径映射的方式,将一个源视图文件路径,映射成一个主题化后的视图文件路径。 因此,[/web/views => /web/themes/basic] 定义了一个主题映射关系, 源视图文件/web/views/site/index.php 主题化后将是 /web/themes/basic/site/index.php 。 因此, Yii1.1中的CThemeManager 也被淘汰了。 ### 模型(Model) > MVC中的M指的就是模型,Yii1.1中使用 CModel 来表示,而Yii2.0使用 yii\\base\\Model 来表示。 > Yii1.1中, CFormModel 用来表示用户的表单输入,以区别于数据库中的表。 这在Yii2.0中也被淘汰,Yii2.0倾向于使用继承自 yii\\base\\Model 来表示提交的表单数据。 > 另外,Yii2.0为Model引入了 yii\\base\\Model::load() 和 yii\\base\\Model::loadMutiple() 两个新的方法, 用于简化将用户输入的表单数据赋值给Model: ``` // Yii0使用load()等同于下面Yii1的用法 $model = new Post; if ($model->load($_POST)) { ... ... } // Yii1中常用的套路 if (isset($_POST['Post'])) { $model->attributes = $_POST['Post']; } ``` > 另外一个重要变化就是Yii2.0中改变了Model应用于不同场景的逻辑。通过引入yii\\base\\Model::scenarios() 来集中管理场景,使得一个Model所有适用的场景都比较清晰,一目了然。而Yii1.1是没有一个统一管理场景的方法的。 > 由此带来的一个很容易出现的问题就是,当你声明一个Model处于某一场景时,可能由于拼写错误, 不小心将场景的名称写错了,那么在Yii1.1中,这个错误的场景并没有任何的提示。假设有以下情况: ``` class UserForm extends CFormModel { public $username; public $email; public $password; public $password_repeat; public $rememberMe=false; public function rules() { return array( // username 和 password 在所有场景中都要验证 array('username, password', 'required'), // email 和 password_repeat 只在注册场景中验证 array('email, password_repeat', 'required', 'on'=>'Registration'), array('email', 'email', 'on'=>'Registration'), // rememberMe 仅在登陆场景中验证 array('rememberMe', 'boolean', 'on'=>'Login'), ); } } ``` > 这里针对UserForm的注册和登陆两个场景,设定了不同的验证规则。接下来,你要在注册场景中使用这个UserForm, 但你一不小心将 Registration 场景设定成了 SignUp , 说实在,我不是学英文出身的,这两个单词的意思在我眼里是一样一样的。只是Yii不会智能到把这两个场景等同起来。 那么Yii1.1将不会有任何的提示,并自动地使用第一个验证规则,而用户注册时填写的 email 和password_repeat 字段就被抛弃了。这在实际编程中,是经常出现的一个低级错误。 > 从这里可以看到,Yii1.1中对于场景,没有一个集中统一的管理,也就是说一个Model可适用的场景, 是不确定的、任意的。通过 rules() 你很难一眼看出来一个Model可以适用于多少个场景,每个场景下都有哪些字段是有效的、需要验证的。 > 而在Yii2.0中,由于引入了 yii\\base\\Model::scenarios() 新的方法, 将本Model所有适用的场景,及不同场景下的有效字段都进行了声明, 这个逻辑就显得清晰了。而且,如果使用了一个未声明的场景,Yii2.0会有相应的提示, 这避免了上面这个低级错误的可能: ``` namespace app\\models; use yii\\db\\ActiveRecord; class User extends ActiveRecord { public function scenarios() { return [ 'login' => ['username', 'password', 'rememberMe'], 'registration' => ['username', 'email', 'password', 'password_repeat'], ]; } } ``` > 这样看来,是不是很清晰?这个User仅有两种场景,每种场景的有效字段也一目了然。 而至于具体场景下每个字段的验证规则,仍然由 yii\\base\\Model::rules() 来确定。 这也意味着, unsafe 验证在Yii2.0中也没有了立足之地,凡是 unsafe 的字段,就不在特定的场景中列出来。 或者为了更加明显的表示某一字段在特定场景下是无效的,可以给这个字段加上 ! 前缀。 > 在默认情况下, yii\\base\\Model::scenarios() 所有适用的场景和对应的字段由yii\\base\\Model::rules() 的内容自动生成。也就是说,如果你的 rules() 很完备、很清晰,那么也是不需要重载这个 scenarios() 的。 这种情况下,Yii1.1和Yii2.0在这一点上的表现形式,是一样的。但是,个人经验看, 我更倾向于将 scenarios() 声明清楚,而在 rules() 中,仅指定字段的验证规则,而不涉及场景的内容。 这样的逻辑更加清晰,便于其他团队成员阅读你的代码,也便于后续的维护和开发。 ### 控制器(Controller) > 除了上面讲到的控制器中要使用 echo 来显示渲染视图的输出这点区别外, Yii1.1与Yii2.0的控制器还表现出更为明显的区别,那就是动作过滤器(Action Filter) 的不同。 > 在Yii2.0中,动作过滤器以行为(behavior)的方式出现, 一般继承自 yii\\base\\ActionFilter ,并注入到一个控制器中,以发生作用。比如,Yii1.1中很常见的: ``` public function behaviors() { return [ 'access' => [ 'class' => 'yii\\filters\\AccessControl', 'rules' => [ ['allow' => true, 'actions' => ['admin'], 'roles' => ['@']], ], ], ]; } ``` > 看着是不是有点像,但又确实不一样? ### Active Record > 还记得么?在Yii1.1中,数据库查询被分散成 CDbCommand , CDbCriteria 和 CDbCommandBuilder 。 所谓天下大势分久必合,到了Yii2.0,采用 yii\\db\\Query 来表示数据库查询: ``` $query = new \\yii\\db\\Query(); $query->select('id, name') ->from('user') ->limit(10); $command = $query->createCommand(); $sql = $command->sql; $rows = $command->queryAll(); ``` > 最最最爽的是, yii\\db\\Query 可以在 Active Record中使用,而在Yii1.1中,要结合两者,并不容易。 > Active Record在Yii2.0中最大的变化一个是查询的构建,另一个是关联查询的处理。 > Yii1.1中的 CDbCriteria 在Yii2.0中被 yii\\db\\ActiveQuery 所取代, 这个把前辈拍死在沙滩上的家伙,继承自 yii\\db\\Query ,所以可以进行类似上面代码的查询。 调用 yii\\db\\ActiveRecord::find() 就可以启动查询的构建了: ``` $customers = Customer::find() ->where(['status' => $active]) ->orderBy('id') ->all(); ``` > 这在Yii1.1中,是不容易实现的。特别是比较复杂的查询关系。 > 在关联查询方面,Yii1.1是在一个统一的地方 relations() 定义关联关系。 而Yii2.0改变了这一做法,定义一个关联关系: * 定义一个getter方法 * getter方法的方法名表示关联关系的名称,如 getOrders() 表示关系 orders * getter方法中定义关联的依据,通常是外键关系 * getter返回一个 yii\\db\\ActiveQuery 对象 > 比如以下代码就定义了 Customer 的 orders 关联关系: ``` class Customer extends \\yii\\db\\ActiveRecord { ... ... public function getOrders() { // 关联的依据是 Order.customer_id = Customer.id return $this->hasMany('Order', ['customer_id' => 'id']); } } ``` > 这样的话,可以通过 Customer 访问关联的 Order ``` // 获取所有与当前 $customer 关联的 orders $orders = $customer->orders; // 获取所有关联 orders 中,status=1 的 orders $orders = $customer->getOrders()->andWhere('status=1')->all(); ``` > 对于关联查询,有积极的方式也有消极的方式。区别在于采用积极方式时,关联的查询会一并执行, 而消极方式时,仅在显示调用关联记录时材会执行关联的查询。 > 在积极方式的实现上,Yii2.0与Yii1.1也存在不同。Yii1.1使用一个JOIN查询, 来实现同时查询主记录及其关联的记录。 而Yii2.0弃用JOIN查询的方式,而使用两个顺序的SQL语句, 第一个语句查询主记录,第二个语句根据第一个语句的返回结果进行过滤。 > 同时,Yii2.0为Active Record引入了 asArray() 方法。在返回大量记录时,可以以数组形式保存, 而不再以对象形式保存,这样可以节约大量的空间,提高效率。 > 另外一个变化是,在Yii1.1中,字段的默认值可以通过为类的public 成员变量赋初始值来指定。 而在Yii2.0中,这样的方式是行不通的,必须通过重载 init() 成员函数的方式实现了。 > Yii2.0还淘汰掉了原来的 CActiveRecordBehavior 类。在Yii2.0中,将行为与类进行绑定采用了统一的方式进行, 具体请参考 _行为(Behavior)_ 的有关内容。 > Yii2.0中,ActiveRecord得到极大的加强,在相关的章节中我们已经进行专门的讲解。 > 这里的内容主要是点一点Yii2.0之于Yii1.1的变化。大致了解下就可以了, 主要还是要看正文专门针对每个知识点的深入讲解。
admin
2022年12月19日 14:13
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码