[Yii2笔记]052行为(Behaviors) [ 技术分享 ]
说明
学习Yii Framework 2(易2框架)的过程是漫长的,也是充满乐趣的,以下是我学习Yii2框架时对官网英文资料(请参见原文网址)的翻译和代码实现,提供了较完整的代码,供你参考。不妥之处,请多多指正!
原文网址:
http://www.yiiframework.com/doc-2.0/guide-concept-behaviors.html
本文主题:行为(Behaviors)
行为(Behavior)是yii\base\Behavior或其子类的实例,行为,以最小化最为出名,允许你扩展已存在的组件类的功能,而无需改变类的继承关系。绑定一个行为到一个组件就是依赖注入(inject)行为的方法和属性到组件中,而这些方法和属性的调用就如同是组件内部定义的一样。行为可以响应组件触发的事件,从而允许行为自定义组件执行的普通代码。
1、Defining Behaviors(定义行为)
要定义一个行为,创建一个继承自yii\base\Behavior或其子类的类文件取出可,例如:
D:\phpwork\advanced\frontend\components\MyBehavior.php
<?php
namespace frontend\components;
use yii\base\Behavior;
class MyBehavior extends Behavior{
public $prop1;
private $_prop2;
public function setProp2($value){
$this->_prop2=$value;
}
public function getProp2(){
return $this->_prop2;
}
public function foo(){
}
}
以上代码定义了一个行为类frontend\components\MyBehavior,和两个属性prop1和prop2,以及一个方法foo()。注意:属性prop2是通过getProp2()和setProp2()来定义的,这种情况是因为yii\base\Behavior继承自yii\base\Object,从而支持通过getter和setter来定义属性。
因为这个类是一个行为,当它绑定到一个组件时,组件就可以拥有属性prop1和prop2,也拥有一个方法foo()。
小贴士:在一个行为中你可以通过yii\base\Behavior::$owner 属性获取行为绑定的组件。
注意:如果行为的yii\base\Behavior::__get()和yii\base\Behavior::_set()方法被重写,你应该也要同时重写yii\base\Behavior::canGetProperty()和yii\base\Beharior::canSetProperty()方法。
2、Handling Component Events(绑定组件事件)
如果一个行为需要响应它所绑定的组件触发的事件,它应该重写yii\base\Behavior::events()方法,例如:
D:\phpwork\advanced\frontend\components\HandleBehavior.php
<?php
namespace frontend\components;
use yii\base\Behavior;
use yii\mongodb\ActiveRecord;
class HandleBehavior extends Behavior{
public function events(){
return [
ActiveRecord::EVENT_BEFORE_VALIDATE=>'beforeValidate',
];
}
public function beforeValidate(){
}
}
events()方法会返回一个事件列表,后面是其对应的事件处理器。上例中声明了一个已存在的事件EVENT_BEFORE_VALIDATE和它的事件处理器beforeValidate(),当定义一个事件处理器时,你可以使用以下的一种格式: 1、一个字符串,指向行为类的一个方法名称,如上例所示。 2、一个数组,包括对象或类名称和一个方法名称(没有圆括号),如:[$object,'methodName']。 3、一个匿名函数。
一个事件处理器的形式如下所示:
function($event){
}
$event 是指事件参数。
绑定事件处理器'dowith'到事件EVENT_DANCE上
D:\phpwork\advanced\frontend\components\EventBehavior.php
public function events(){
$parent=parent::events();
$events=[
DanceEventInterface::EVENT_DANCE=>'dowith',
];
return array_merge($parent,$events);
}
3、Attaching Behaviors(绑定行为)
我们可以绑定一个行为到组件,绑定方式可以是静态的,也可以是动态的,在操作中前者更常用些。 要静态绑定一个行为,重写要绑定的组件类的behaviors()即可。behaviors()方法会返回一个行为配置列表,每个行为配置可以是一个行为类名称或一个配置数组: D:\phpwork\advanced\frontend\models\ComModel.php
<?php
namespace frontend\models;
use yii\base\Component;
use frontend\components\MyBehavior;
class ComModel extends Component{
public function behaviors(){
return [
//声明一个匿名行为,只有行为的类名称
MyBehavior::className(),
//声明一个命名的行为,仅指定行为的类名称
'myBehavior2'=>MyBehavior::className(),
//声明一个匿名行为,配置行为数组
[
'class'=>MyBehavior::className(),
'prop1'=>'value1',
'prop2'=>'value2',
],
//声明一个命名的行为,配置行为数组
'myBehavior3'=>[
'class'=>MyBehavior::className(),
'prop1'=>'value3',
'prop2'=>'value4',
],
];
}
}
在行为配置中,你可以定义一个数组键名绑定一个行为,这种情况下,此行为被称为命名的行为,在上例中,有两个命名的行为:myBehavior2和myBehavior4。如果行为没有与名字关联,它被称为匿名行为。
如果要动态绑定一个行为,可以调用要绑定的组件的yii\base\Component::attachBehavior()方法。
//绑定到一个行为类
$this->attachBehavior('mybehavior1',MyBehavior::className());
//绑定到一个对象
$this->attachBehavior('mybehavior2',new MyBehavior());
//绑定到配置数组
$this->attachBehavior('mybehavior3',[
'class'=>MyBehavior::className(),
'prop1'=>'var1',
'prop2'=>'var2',
]);
我们还可以使用yii\base\Component::attachBehaviors()方法一次绑定多个行为:
$this->attachBehaviors([
'mybehavior3'=>[
'class'=>MyBehavior::className(),
'prop1'=>'var1',
'prop2'=>'var2',
],
YouBehavior::className(),
]);
使用组件配置绑定行为的代码如下:
[
'as myBehavior2' => MyBehavior::className(),
'as myBehavior3' => [
'class' => MyBehavior::className(),
'prop1' => 'value1',
'prop2' => 'value2',
],
]
D:\phpwork\advanced\frontend\controllers\PostController.php
public function actionDynamic(){
$config=[
'as behavior'=>[
'class'=>MyBehavior::className(),
'prop2'=>'hello behavior',
],
];
$dynamic=new Dynamic($config);
$dynamic->foo();
}
4、Using Behaviors
要使用一个行为,首先要如上所述绑定行为到组件上,一旦一个行为绑定到组件后,就可以直接调用了。 我们可以调用组件绑定的行为的公共成员变量(public member property)或者是一个使用getter或setter定义的属性(property):
//prop1是定义在行为中的一个属性:
echo $component->prop1;
$component->prop1 = $value;
我们可以调用行为的公共方法:
//foo()是行为类中定义的一个公共方法:
$component->foo();
正如你所看到的,虽然$component 没有定义prop1和foo(),但绑定行为后,它们能够如同组件的一部分一样被使用。 如果两个行为定义了相同的属性或方法,并且绑定到了同一个组件上,第一个绑定到组件上的行为将获得优先权,也就是说,第一个绑定的组件的属性或方法将被获取到。
当行为绑定到组件时可以赋一个名称,在这种情总下,我们可以通过名称来获取这个行为对象:
$behavior = $component->getBehavior('myBehavior');
我们也可以获取绑定到一个组件上的所有的行为:
$behaviors = $component->getBehaviors();
5、Detaching Behaviors(解除绑定的行为)
要解除绑定的一个行为,我们可以调用yii\base\Component::detachBehavior(),需要指定行为的名称。
$component->detachBehavior('myBehavior1');
我们可以解除绑定的所有行为:
$component->detachBeahviors();
6、Using TimestampBehavior
我们现在一起来看一下yii\behaviors\TimestampBehavior实例,这个行为支持自动更新Active Record的时间戳属性,无论此模型是调用insert()、update(),还是save()方法都会更新。 首先绑定这个行为到要使用的Active Record类上:
namespace app\models\User;
use yii\db\ActiveRecord;
use yii\behaviors\TimestampBehavior;
class User extends ActiveRecord{
//自动更新`created_at`(创建时间), `updated_at`(修改时间)的时间戳
public function behaviors()
{
return [
[
//默认字段名:created_at,updated_at
'class' => \yii\behaviors\TimestampBehavior::className(),
],
[
//默认字段名:created_by,updated_by
'class' => \yii\behaviors\BlameableBehavior::className(),
'createdByAttribute' => 'userId',
'updatedByAttribute' => 'updaterId',
],
];
}
//以上的behaviors()函数等效于:
public function behaviors()
{
return [
[
'class' => TimestampBehavior::className(),
'attributes' => [
ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],
],
// if you're using datetime instead of UNIX timestamp:
// 'value' => new Expression('UNIX_TIMESTAMP()'),
],
];
}
//自定义 创建时间和修改时间 的字段名
public function behaviors(){
return [
[
'class' => TimestampBehavior::className(),
//'createdAtAttribute' => false,//如果没有字段需要更新,可以设置为false
'createdAtAttribute' => 'create_time',
'updatedAtAttribute' => 'update_time',
'value' => new yii\db\Expression('UNIX_TIMESTAMP()'),
],
];
}
}
上述代码中的行为配置定义了要操作的记录是以下情况: 1、插入的(inserted),此行为将给created_at和updated_at属性赋值为UNIX的当前时间戳。 2、更新的(updated),此行为将给updated_at属性赋值为UNIX的当前时间戳。
注意:以上实现是完成于MySQL数据库,需要定义两个字段(create_at和updated_at),int(11),用于存储UNIX时间戳。
通过以上代码设置,我们创建一个User对象并保存它,你会发现它的created_at和updated_at将自动填充为当前的UNIX时间戳。
$user = new User;
$user->email = 'test@example.com';
$user->save();
echo $user->created_at; // shows the current timestamp
TimestampBehavior还提供了一个通用方法touch(),它会将当前的时间戳赋值到一个指定的属性,并保存到数据库中:
$user->touch("login_time");
7、Other behaviors(其他行为)
Yii2还提供了一些内置和扩展的行为可以使用: yii\behaviors\BlameableBehavior:自动将当前用户的ID填充到指定属性。 http://www.yiiframework.com/doc-2.0/yii-behaviors-blameablebehavior.html
public function behaviors()
{
return [
[
'class' => \yii\behaviors\BlameableBehavior::className(),
'createdByAttribute' => 'createUserId',//定义创建时要更新的创建者id的属性名称,默认名称是:created_by
'updatedByAttribute' => 'updateUserId',//定义编辑时要更新的编辑者id的属性名称,默认名称是:updated_by
],
];
}
yii\behaviors\SluggableBehavior:自动将URL片段填充到指定属性。 http://www.yiiframework.com/doc-2.0/yii-behaviors-sluggablebehavior.html
yii\behaviors\AttributeBehavior:在特定事件中,自动给指定的一个或多个属性赋一个值。 http://www.yiiframework.com/doc-2.0/yii-behaviors-attributebehavior.html
yii2tech\ar\softdelete\SoftDeleteBehavior:为需要软删除(soft-delete)和软存储(soft-restor)的ActiveRecord提供了方法。 https://github.com/yii2tech/ar-softdelete
yii2tech\ar\position\PositionBehavior:通过重新排序的方法来管理记录的排序,此排序是一个整数字段(integer field) https://github.com/yii2tech/ar-position
8、Comparing Behaviors with Traits(行为和Trait的比较)
行为和tait非常相似,它们都依赖注入(inject)它们的属性和方法到主类中。但它们也有很大的区别,接下来我们就讲述一下它们各自的利弊,它们看起来更象是可以相互替代的组件。
使用行为的理由:
1、行为类更象是一个普通类,支持继承;另一方面,Trait被视为PHP语言支持的拷贝,但不支持继承。 2、行为可以被动态的绑定和解绑,而无需修改组件类;使用trait,你必须修改调用类的代码。 3、行为是可配置的,而trait不行。 4、在组件的事件中调用行为,可以自定义行为的执行代码。 5、当绑定到组件中的行为有名称冲突时,首先绑定到组件的行为将获得更高优先级,这样名称冲突将被自动解决;而在不同的trait中造成的类似冲突只能通过修改属性或方法名称来手动解决。
使用Trait的原因:
1、行为是对象,会占用较多的CPU时间和内存,而Trait更高效。 2、因为Trait是原生语言结构,所以IDE对trait支持更友好一些。
//++++++++++++++++++++++++++++++++++++++
以下是behavior的应用实例
//--------------- //实例,同名属性和同名方法,先绑定的行为的将生效 D:\phpwork\advanced\frontend\controllers\PostController.php
public function actionDynamic(){
$dynamic=new Dynamic();
$dynamic->foo();
$dynamic->bar();
echo "<br>prop2:".$dynamic->prop2;
}
D:\phpwork\advanced\frontend\models\Dynamic.php
<?php
namespace frontend\models;
use yii\base\Component;
use frontend\components\MyBehavior;
use frontend\components\YouBehavior;
class Dynamic extends Component{
public function init(){
parent::init();
//一次绑定到多个行为
$this->attachBehaviors([
'mybehavior3'=>[
'class'=>MyBehavior::className(),
'prop2'=>'var88',//同名属性先注册的生效
],
[
'class'=>YouBehavior::className(),
'prop2'=>'var2',
],
]);
}
}
D:\phpwork\advanced\frontend\components\MyBehavior.php
<?php
namespace frontend\components;
use yii\base\Behavior;
class MyBehavior extends Behavior{
private $_prop2;
public function setProp2($value){
$this->_prop2=$value;
}
public function getProp2(){
return $this->_prop2;
}
public function foo(){
echo "this is MyBehavior's foo()<br>";
}
public function bar(){
//同名方法,先绑定的行为的方法将生效
echo "this is MyBehavior's bar()<br>";
}
}
D:\phpwork\advanced\frontend\components\YouBehavior.php
<?php
namespace frontend\components;
use yii\base\Behavior;
class YouBehavior extends Behavior{
public $prop2;
public function bar(){
echo "this is YouBehavior's bar()<br>";
$class=$this->owner;
echo "It's owner:".$class->className()."<br>";
}
}
测试结果:
http://localhost:8082/post/dynamic
/*
this is MyBehavior's foo()
this is MyBehavior's bar()
prop2:var88
*/
//解除绑定的'mybehavior3'行为,使用上例中的其他程序 http://localhost:8082/post/dynamic
public function actionDynamic(){
$dynamic=new Dynamic();
$dynamic->foo();
//解除绑定的'mybehavior3'行为
$dynamic->detachBehavior('mybehavior3');
$dynamic->bar();
echo "<br>prop2:".$dynamic->prop2;
}
测试结果:
/*
this is MyBehavior's foo()
this is YouBehaviors bar()
It's owner:frontend\models\Dynamic
prop2:var2
*/
//--------------- //实例,使用组件配置来绑定行为 D:\phpwork\advanced\frontend\controllers\PostController.php
public function actionDynamic(){
$config=[
//为组件定义了一个绑定的行为MyBehavior,并为参数prop2赋了初值
'as behavior'=>[
'class'=>MyBehavior::className(),
'prop2'=>'hello behavior',
],
];
//调用配置信息初始化Dynamic组件
$dynamic=new Dynamic($config);
$dynamic->foo();
}
D:\phpwork\advanced\frontend\models\Dynamic.php
<?php
namespace frontend\models;
use yii\base\Component;
class Dynamic extends Component{
//Dynamic是个空组件
}
D:\phpwork\advanced\frontend\components\MyBehavior.php
<?php
namespace frontend\components;
use yii\base\Behavior;
class MyBehavior extends Behavior{
public $prop1;
private $_prop2;
public function setProp2($value){
$this->_prop2=$value;
}
public function getProp2(){
return $this->_prop2;
}
public function foo(){
echo "this is MyBehavior's foo()<br>";
$class=$this->owner;
echo "It's owner:".$class->className()."<br>";
echo "prop2:".$this->prop2."<br>";
}
}
测试结果:
http://localhost:8082/post/dynamic
/*
this is MyBehavior's foo()
It's owner:frontend\models\Dynamic
prop2:hello behavior
*/
//--------------- //实例,绑定一个行为到组件的事件中 D:\phpwork\advanced\frontend\controllers\PostController.php
public function actionEventBehavior(){
echo "actionEventBehavior<br>";
$eventModel=new EventModel();
//触发行为
$eventModel->trigger(DanceEventInterface::EVENT_DANCE);
}
D:\phpwork\advanced\frontend\models\EventModel.php
<?php
namespace frontend\models;
use frontend\components\EventBehavior;
use yii\base\Component;
class EventModel extends Component{
public function init(){
parent::init();
//绑定行为到配置数组
$this->attachBehavior('mybehavior3',[
'class'=>EventBehavior::className(),
]);
}
}
D:\phpwork\advanced\frontend\components\EventBehavior.php
<?php
namespace frontend\components;
use frontend\models\DanceEventInterface;
use yii\base\Behavior;
class EventBehavior extends Behavior implements DanceEventInterface{
public function events(){
$parent=parent::events();
//将自定义的事件处理器'dowith'与事件EVENT_DANCE绑定
$events=[
DanceEventInterface::EVENT_DANCE=>'dowith',
];
return array_merge($parent,$events);
}
//事件处理器代码:
public function dowith($event){
echo "EVENT_DANCE handler";
}
public function foo(){
echo "this is EventBehavior's foo()<br>";
}
}
测试结果:
http://localhost:8082/post/event-behavior
/*
actionEventBehavior
EVENT_DANCE handler
*/
//--------------- //实例,绑定behavior的实例,绑定多个行为到一个组件 D:\phpwork\advanced\frontend\controllers\PostController.php
public function actionDynamic(){
$dynamic=new Dynamic();
$dynamic->foo();
$dynamic->bar();
}
D:\phpwork\advanced\frontend\models\Dynamic.php
<?php
namespace frontend\models;
use yii\base\Component;
use frontend\components\MyBehavior;
use frontend\components\YouBehavior;
class Dynamic extends Component{
public function init(){
parent::init();
//针对于同一个行为的配置不应重复,以下代码采用其中一种即可
//绑定到一个行为类
$this->attachBehavior('mybehavior1',MyBehavior::className());
//绑定到一个对象
$this->attachBehavior('mybehavior2',new MyBehavior());
//绑定到配置数组
$this->attachBehavior('mybehavior3',[
'class'=>MyBehavior::className(),
'prop1'=>'var1',
'prop2'=>'var2',
]);
//一次绑定到多个行为
$this->attachBehaviors([
'mybehavior3'=>[
'class'=>MyBehavior::className(),
'prop1'=>'var1',
'prop2'=>'var2',
],
YouBehavior::className(),
]);
}
}
D:\phpwork\advanced\frontend\components\MyBehavior.php
<?php
namespace frontend\components;
use yii\base\Behavior;
class MyBehavior extends Behavior{
public $prop1;
private $_prop2;
public function setProp2($value){
$this->_prop2=$value;
}
public function getProp2(){
return $this->_prop2;
}
public function foo(){
echo "this is MyBehavior's foo()<br>";
$class=$this->owner;
echo "It's owner:".$class->className()."<br>";
echo "prop2:".$this->prop2."<br>";
}
}
D:\phpwork\advanced\frontend\components\YouBehavior.php
<?php
namespace frontend\components;
use yii\base\Behavior;
class YouBehavior extends Behavior{
public $prop3;
private $_prop4;
public function setProp4($value){
$this->_prop4=$value;
}
public function getProp4(){
return $this->_prop4;
}
public function bar(){
echo "this is YouBehavior's bar()<br>";
$class=$this->owner;
echo "It's owner:".$class->className()."<br>";
}
}
测试结果:
http://localhost:8082/post/dynamic
/*
this is MyBehavior's foo()
It's owner:frontend\models\Dynamic
prop2:
this is YouBehavior's bar()
It's owner:frontend\models\Dynamic
*/
//--------------- //实例:behavior的基础应用,绑定一个行为到一个组件 D:\phpwork\advanced\frontend\controllers\PostController.php
public function actionComModel(){
//创建一个组件
$comModel=new ComModel();
//调用组件绑定的behavior的方法:
$comModel->foo();
}
D:\phpwork\advanced\frontend\components\MyBehavior.php
<?php
namespace frontend\components;
use yii\base\Behavior;
class MyBehavior extends Behavior{
public $prop1;
private $_prop2;
public function setProp2($value){
$this->_prop2=$value;
}
public function getProp2(){
return $this->_prop2;
}
public function foo(){
echo "this is MyBehavior's foo()<br>";
//获取调用本behavior的component
$class=$this->owner;
//显示组件的类名称
echo "It's owner:".$class->className()."<br>";
echo "prop2:".$this->prop2."<br>";
}
}
D:\phpwork\advanced\frontend\models\ComModel.php
<?php
namespace frontend\models;
use yii\base\Component;
use frontend\components\MyBehavior;
class ComModel extends Component{
public function behaviors(){
return [
//针对于同一个行为的配置不应重复,以下1-4配置代码采用其中一种即可
//1、声明一个匿名行为,只有行为的类名称
MyBehavior::className(),
//2、声明一个命名的行为,仅指定行为的类名称
'myBehavior2'=>MyBehavior::className(),
//3、声明一个匿名行为,配置行为数组
[
'class'=>MyBehavior::className(),
'prop1'=>'value1',
'prop2'=>'value2',
],
//4、声明一个命名的行为,配置行为数组
'myBehavior3'=>[
'class'=>MyBehavior::className(),
'prop1'=>'value3',
'prop2'=>'value4',
],
];
}
}
//在behaviors()中重复配置同一个Behavior时的生效规则: 类名称》数组配置,同时存在时,“类名称”生效 匿名行为》命名行为,同时存在时,“匿名行为”生效 根据以上规则,本例中最终MyBehavior::className()配置生效,所以prop2为空。
测试结果:
http://localhost:8082/post/com-model
/*
this is MyBehavior's foo()
It's owner:frontend\models\ComModel
prop2:
*/
//仅配置一次行为时,prop2被设置为'value4',可以生效
public function behaviors(){
return [
//声明一个命名的行为,配置行为数组
'myBehavior3'=>[
'class'=>MyBehavior::className(),
'prop1'=>'value3',
'prop2'=>'value4',
],
];
}
测试结果:
/*
this is MyBehavior's foo()
It's owner:frontend\models\ComModel
prop2:value4
*/
(全文完)
共 2 条回复
-
NingerJohn 回复于 2017-12-12 11:41 举报
你好,我在model里面配置了TimestampBehavior,更新的时候,update_time字段确实可以自动更新。那么在某些时候,我想临时取消TimestampBehavior行为,应该怎么操作呢?试过了detachBehavior,最终save的时候,update_time字段还是会自动更新
`public function behaviors() { return [ [ 'class'=>TimestampBehavior::className(), 'attributes' => [ ActiveRecord::EVENT_BEFORE_INSERT=>['add_time', 'update_time'], ActiveRecord::EVENT_BEFORE_UPDATE=>['update_time'], ], ], [ 'class'=>ProjectBehavior::className() ], ]; }
`
-
你好,我也写了关于行为的文章,
Yii2基本概念之——行为(Behavior)(http://www.yiichina.com/tutorial/1629)
可以相互学习下!
阿江
最后登录:2024-03-03
在线时长:186小时21分
- 粉丝94
- 金钱16816
- 威望160
- 积分20276