我们先来看下行为在 Yii2 中的使用,如下内容摘自 Yii2中文文档

行为是 [[yii\base\Behavior]] 或其子类的实例。行为,也称为 mixins,可以无须改变类继承关系即可增强一个已有的 [[yii\base\Component|组件]] 类功能。当行为附加到组件后,它将“注入”它的方法和属性到组件,然后可以像访问组件内定义的方法和属性一样访问它们。此外,行为通过组件能响应被触发的事件,从而自定义或调整组件正常执行的代码。

定义行为

要定义行为,通过继承 [[yii\base\Behavior]] 或其子类来建立一个类。如:

  1. namespace app\components;
  2. use yii\base\Behavior;
  3. class MyBehavior extends Behavior
  4. {
  5. public $prop1;
  6. private $_prop2;
  7. public function getProp2()
  8. {
  9. return $this->_prop2;
  10. }
  11. public function setProp2($value)
  12. {
  13. $this->_prop2 = $value;
  14. }
  15. public function foo()
  16. {
  17. // ...
  18. }
  19. }

以上代码定义了行为类 app\components\MyBehavior 并为要附加行为的组件提供了两个属性 prop1prop2 和一个方法 foo() 。注意属性 prop2 是通过 getter getProp2() 和 setter setProp2() 定义的。能这样用是因为 [[yii\base\Object]] 是 [[yii\base\Behavior]] 的祖先类,此祖先类支持用 getter 和 setter 方法定义属性

Tip: 在行为内部可以通过 [[yii\base\Behavior::owner]] 属性访问行为已附加的组件。

处理事件

如果要让行为响应对应组件的事件触发,就应覆写 [[yii\base\Behavior::events()]] 方法,如:

  1. namespace app\components;
  2. use yii\db\ActiveRecord;
  3. use yii\base\Behavior;
  4. class MyBehavior extends Behavior
  5. {
  6. // 其它代码
  7. public function events()
  8. {
  9. return [
  10. ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',
  11. ];
  12. }
  13. public function beforeValidate($event)
  14. {
  15. // 处理器方法逻辑
  16. }
  17. }

[[yii\base\Behavior::events()|events()]] 方法返回事件列表和相应的处理器。上例声明了 [[yii\db\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]] 事件和它的处理器 beforeValidate() 。当指定一个事件处理器时,要使用以下格式之一:

  • 指向行为类的方法名的字符串,如上例所示;
  • 对象或类名和方法名的数组,如 [$object, 'methodName']
  • 匿名方法。

处理器的格式如下,其中 $event 指向事件参数。关于事件的更多细节请参考事件

  1. function ($event) {
  2. }

附加行为

可以静态或动态地附加行为到[[yii\base\Component|组件]]。前者在实践中更常见。

要静态附加行为,覆写行为要附加的组件类的 [[yii\base\Component::behaviors()|behaviors()]] 方法即可。[[yii\base\Component::behaviors()|behaviors()]] 方法应该返回行为配置列表。每个行为配置可以是行为类名也可以是配置数组。如:

  1. namespace app\models;
  2. use yii\db\ActiveRecord;
  3. use app\components\MyBehavior;
  4. class User extends ActiveRecord
  5. {
  6. public function behaviors()
  7. {
  8. return [
  9. // 匿名行为,只有行为类名
  10. MyBehavior::className(),
  11. // 命名行为,只有行为类名
  12. 'myBehavior2' => MyBehavior::className(),
  13. // 匿名行为,配置数组
  14. [
  15. 'class' => MyBehavior::className(),
  16. 'prop1' => 'value1',
  17. 'prop2' => 'value2',
  18. ],
  19. // 命名行为,配置数组
  20. 'myBehavior4' => [
  21. 'class' => MyBehavior::className(),
  22. 'prop1' => 'value1',
  23. 'prop2' => 'value2',
  24. ]
  25. ];
  26. }
  27. }

通过指定行为配置数组相应的键可以给行为关联一个名称。这种行为称为命名行为。上例中,有两个命名行为:myBehavior2myBehavior4 。如果行为没有指定名称就是匿名行为

要动态附加行为,在对应组件里调用 [[yii\base\Component::attachBehavior()]] 方法即可,如:

  1. use app\components\MyBehavior;
  2. // 附加行为对象
  3. $component->attachBehavior('myBehavior1', new MyBehavior);
  4. // 附加行为类
  5. $component->attachBehavior('myBehavior2', MyBehavior::className());
  6. // 附加配置数组
  7. $component->attachBehavior('myBehavior3', [
  8. 'class' => MyBehavior::className(),
  9. 'prop1' => 'value1',
  10. 'prop2' => 'value2',
  11. ]);

可以通过 [[yii\base\Component::attachBehaviors()]] 方法一次附加多个行为:

  1. $component->attachBehaviors([
  2. 'myBehavior1' => new MyBehavior, // 命名行为
  3. MyBehavior::className(), // 匿名行为
  4. ]);

还可以通过配置去附加行为:

  1. [
  2. 'as myBehavior2' => MyBehavior::className(),
  3. 'as myBehavior3' => [
  4. 'class' => MyBehavior::className(),
  5. 'prop1' => 'value1',
  6. 'prop2' => 'value2',
  7. ],
  8. ]

使用行为

使用行为,必须像前文描述的一样先把它附加到 [[yii\base\Component|component]] 类或其子类。一旦行为附加到组件,就可以直接使用它。

行为附加到组件后,可以通过组件访问一个行为的公共成员变量或 getter 和 setter 方法定义的属性:

  1. // "prop1" 是定义在行为类的属性
  2. echo $component->prop1;
  3. $component->prop1 = $value;

类似地也可以调用行为的公共方法:

  1. // foo() 是定义在行为类的公共方法
  2. $component->foo();

如你所见,尽管 $component 未定义 prop1foo() ,它们用起来也像组件自己定义的一样。

如果两个行为都定义了一样的属性或方法,并且它们都附加到同一个组件,那么首先附加上的行为在属性或方法被访问时有优先权。

附加行为到组件时的命名行为,可以使用这个名称来访问行为对象,如下所示:

  1. $behavior = $component->getBehavior('myBehavior');

也能获取附加到这个组件的所有行为:

  1. $behaviors = $component->getBehaviors();

移除行为

要移除行为,可以调用 [[yii\base\Component::detachBehavior()]] 方法用行为相关联的名字实现:

  1. $component->detachBehavior('myBehavior1');

也可以移除全部行为:

  1. $component->detachBehaviors();

使用 TimestampBehavior

这个行为支持在 [[yii\db\ActiveRecord|Active Record]] 存储时自动更新它的时间戳属性。

首先,附加这个行为到计划使用该行为的 [[yii\db\ActiveRecord|Active Record]] 类:

  1. namespace app\models\User;
  2. use yii\db\ActiveRecord;
  3. use yii\behaviors\TimestampBehavior;
  4. class User extends ActiveRecord
  5. {
  6. // ...
  7. public function behaviors()
  8. {
  9. return [
  10. [
  11. 'class' => TimestampBehavior::className(),
  12. 'attributes' => [
  13. ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
  14. ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],
  15. ],
  16. ],
  17. ];
  18. }
  19. }

以上指定的行为数组:

  • 当记录插入时,行为将当前时间戳赋值给 created_atupdated_at 属性;
  • 当记录更新时,行为将当前时间戳赋值给 updated_at 属性。

保存 User 对象,将会发现它的 created_atupdated_at 属性自动填充了当前时间戳:

  1. $user = new User;
  2. $user->email = 'test@example.com';
  3. $user->save();
  4. echo $user->created_at; // 显示当前时间戳

[[yii\behaviors\TimestampBehavior|TimestampBehavior]] 行为还提供了一个有用的方法 [[yii\behaviors\TimestampBehavior::touch()|touch()]],这个方法能将当前时间戳赋值给指定属性并保存到数据库:

  1. $user->touch('login_time');

使用 AttributeBehavior

这个行为支持在 [[yii\db\ActiveRecord|Active Record]] 事件触发时自动修改它的属性。

首先,附加这个行为到计划使用该行为的 [[yii\db\ActiveRecord|Active Record]] 类:

  1. namespace app\models\User;
  2. use yii\db\ActiveRecord;
  3. use yii\behaviors\TimestampBehavior;
  4. class User extends ActiveRecord
  5. {
  6. // ...
  7. public function behaviors()
  8. {
  9. return [
  10. [
  11. 'class' => AttributeBehavior::className(),
  12. 'attributes' => [
  13. ActiveRecord::EVENT_BEFORE_INSERT => 'attribute1',
  14. ActiveRecord::EVENT_BEFORE_UPDATE => 'attribute2',
  15. ],
  16. 'value' => function ($event) {
  17. return 'some value';
  18. },
  19. ],
  20. ];
  21. }
  22. }

以上指定的行为数组:

  • 当记录插入时,行为将value的返回值给 attribute1 属性;
  • 当记录更新时,行为将value的返回值给 attribute2 属性;

与 PHP traits 的比较

尽管行为在 "注入" 属性和方法到主类方面类似于 traits ,它们在很多方面却不相同。如上所述,它们各有利弊。它们更像是互补的而不是相互替代。

行为的优势

行为类像普通类支持继承。另一方面,traits 可以视为 PHP 语言支持的复制粘贴功能,它不支持继承。

行为无须修改组件类就可动态附加到组件或移除。要使用 traits,必须修改使用它的类。

行为是可配置的而 traits 不能。

行为以响应事件来自定义组件的代码执行。

当不同行为附加到同一组件产生命名冲突时,这个冲突通过先附加行为的优先权自动解决。而由不同 traits 引发的命名冲突需要通过手工重命名冲突属性或方法来解决。

traits 的优势

traits 比起行为更高效,因为行为是对象,消耗时间和内存。

IDE 对 traits 更友好,因为它们是语言结构。

Yii2的深入学习--行为Behavior的更多相关文章

  1. Yii2的深入学习--继承关系

    想要了解 Yii2 的话,一定要对 Yii2 中相关类的继承关系有所了解.由于暂时读的代码有限,下面的图中只列出了部分继承关系,之后回跟着源码阅读的越来越多而增加 由上图可以看到 Yii2 中大多数类 ...

  2. Yii2的深入学习--入口文件

    前一段时间,尝试去写一个 php 的简单框架,发现自己还欠缺很多,就暂时停掉了.准备先读完 Yii2 的源码,然后再去看完 laravel 的源码,最后再继续去写这个简单的 php 框架. 之后关于 ...

  3. yii2源码学习笔记(十一)

    Controller控制器类,是所有控制器的基类,用于调用模型和布局. <?php /** * @link http://www.yiiframework.com/ * @copyright C ...

  4. yii2源码学习笔记(八)

    Action是所有控制器的基类,接下来了解一下它的源码.yii2\base\Action.php <?php /** * @link http://www.yiiframework.com/ * ...

  5. yii2源码学习笔记(六)

    Behvaior类,Behavior类是所有事件类的基类: 目录yii2\base\Behavior.php <?php /** * @link http://www.yiiframework. ...

  6. Yii2的相关学习记录,下载Yii2(一)

    原先学习过Yii1的相关知识,虽然也是半懂不懂的,但稍微的结构是了解的.现在利用晚上的时间学习下Yii2的使用,打算建一个后台管理系统,这里记录下,以免自己以后忘记. 目前已看一部分Yii2的权威指南 ...

  7. Yii2.0源码阅读-behavior的实现原理

    Yii2.0中的一个思想就是组件化的思想,所以.大多数的类都直接或间接的继承自yii\base\Component,而组件的三大功能:属性.事件.行为. 行为的目的是为了方便的扩展一个类的功能,而不需 ...

  8. Yii2基本概念之——行为(Behavior)

    使用行为(behavior)可以在不修改现有类的情况下,对类的功能进行扩充.通过将行为绑定到一个类,可以使得类具有行为本身所具有的属性和方法,就好像是类本来就具有的这些属性和功能一样. 好的代码设计, ...

  9. Yii2的深入学习--事件Event

    我们先来看下事件在 Yii2 中的使用,如下内容摘自 Yii2中文文档 事件可以将自定义代码“注入”到现有代码中的特定执行点.附加自定义代码到某个事件,当这个事件被触发时,这些代码就会自动执行.例如, ...

随机推荐

  1. 【AR实验室】ARToolKit之概述篇

    0x00 - 前言 我从去年就开始对AR(Augmented Reality)技术比较关注,但是去年AR行业一直处于偶尔发声的状态,丝毫没有其"异姓同名"的兄弟VR(Virtual ...

  2. TechEmpower 13轮测试中的ASP.NET Core性能测试

    应用性能直接影响到托管服务的成本,因此公司在开发应用时需要格外注意应用所使用的Web框架,初创公司尤其如此.此外,糟糕的应用性能也会影响到用户体验,甚至会因此受到相关搜索引擎的降级处罚.在选择框架时, ...

  3. 采用MiniProfiler监控EF与.NET MVC项目(Entity Framework 延伸系列1)

    前言 Entity Framework 延伸系列目录 今天来说说EF与MVC项目的性能检测和监控 首先,先介绍一下今天我们使用的工具吧. MiniProfiler~ 这个东西的介绍如下: MVC Mi ...

  4. ABP文档 - Javascript Api - Message

    本节内容: 显示信息 确认 Message API给用户显示一个信息,或从用户那里获取一个确认信息. Message API默认使用sweetalert实现,为使sweetalert正常工作,你应该包 ...

  5. 【开源】.net 分布式架构之监控平台

    开源地址:http://git.oschina.net/chejiangyi/Dyd.BaseService.Monitor .net 简单监控平台,用于集群的性能监控,应用耗时监控管理,统一日志管理 ...

  6. Ajax实现原理,代码封装

    都知道实现页面的异步操作需要使用Ajax,那么Ajax到是怎么实现异步操作的呢? 首先需要认识一个对象 --> XMLHttpRequest 对象 --> Ajax的核心.它有许多的属性和 ...

  7. notepad++设置默认打开txt文件失效的解决方法

    1.系统环境 win10企业版,64位系统 2.初步设置 设置txt默认为notepad++打开,菜单:设置->首选项->文件关联 选择对应的文件扩展,点击"关闭"按钮 ...

  8. C# 数组的交集、差集、并集

    C# 数组的交集.差集.并集 工作中经常会用这方面的知识来检查那些字段是必须输入的,那些是禁止输入. using System; using System.Collections.Generic; u ...

  9. 水平可见直线 bzoj 1007

    水平可见直线 (1s 128M) lines [问题描述] 在xoy直角坐标平面上有n条直线L1,L2,...Ln,若在y值为正无穷大处往下看,能见到Li的某个子线段,则称Li为可见的,否则Li为被覆 ...

  10. 浏览器的兼容模式下的button中文字垂直方向不居中显示

    <button style="cursor:pointer;vertical-align: middle;" >删除</button> 这时候垂直不居中. ...