首先,事件和行为是yii2框架下,组件类才拥有的功能。而组件类,即是yii\base\Component 类或其子类的实例。所以,yii2框架下,大部分的类都可以使用事件和行为。
什么是事件
事件是给指定实例或类绑定一个回调函数,在需要的时候,使用trigger调用即可。
yii\base\Controller有内置的beforeAction、afterAction方法;
BaseActiveRecord内置的有afterFind,beforeInsert,afterInsert等方法
yii\base\Model有beforeValidate、afterValidate等方法;
这些方法中,使用trigger调用了和方法名相同的事件。如果没有找到绑定的事件,则不执行。
使用事件的3种方式
如何让一个对象执行事件呢?有三种方法
1、覆盖类中内置的调用事件的方法
上面列举了几种基类内置的调用绑定事件的方法。我们可以在子类中重写这些方法,在方法中书写我们想要执行的代码即可。这样跳就跳过了先绑定事件,在用tigger调用的步骤,简单粗暴
class user extends ActiveRecord{
public function beforeSave(){
$this->updated_at = time();
}
}
beforeSave是user祖先类中内置的,在保存数据前调用的方法。在祖先类中,该方法内使用tigger调用绑定的
beforeSave事件。
而我们在上面的代码中,重载了祖先类的beforeSave()方法,使用自定义代码实现了beforeSave
2、使用on绑定事件
class user extends ActiveRecord{}
$user = new user();
$user->on('beforeSave',function()use($user){$user->updated_at = time()});
$user->save();
3、使用行为
我们看到,前两个方法虽然简单直接,但是有一个弊端,就是不能代码复用。如果有很多model都需要自动插入更新时间的话,就需要在所有model下都重复编写beforeSave方法。
这个时候行为就派上用场了
行为类,类似一个Trait。当一个类引入一个行为类后,就可以使用行为类中的属性和方法。
同时,行为类提供了一个events()方法,用以绑定事件
//建立一个行为类
class AutoTimeBehavior extends Behavior{
public function events(){
return [
'beforeSave' => 'setUpdateTime',
'mine' =>'formatResult',
];
}
//实现绑定到事件的方法
//$event是一个Event对象,表示当前调用事件对象,包含事件名称等信息
public function setUpdateTime($event){
//验证前执行的事件
//$this->owner 是调用当前行为的model或controller对象,可以直接调用对象中的属性和方法
$this->owner->updated_at = time();
}
public function formatResult(){
//我的自定义事件
}
}
使用行为类
class user extends ActiveRecord{
public function behaviors(){
return array_merge(parent::behaviors(),[
['class' => 'AutoTimeBehavior'],
]);
}
}
$user = new user();
$user->tigger('mine');
$user->save();
行为中绑定的beforeSave事件,会被user中的内置方法调用。而mine事件因为没内置方法调用,所以需要我们在外部自行调用。也可以在user类或祖先类中,在合适的位置使用tigger调用自定义事件
Yii2预定义的一些行为
我们只要在控制器中设施好这些行为,不需要在代码中自己去执行,框架在运行时,会自动在预定义这些行为的位置执行相关的方法。
控制器行为
public function behaviors(){
$behaviors = parent::behaviors();
//设置跨域请求相关,一般用在接口类控制器接口的基类中
$behaviors['cors'] = [
'class' => \tesoon\filters\Cors::className(),
'cors' => [
//设置允许跨域请求的域名,*表示不限
'Origin' => ['*'],
//设置允许的请求方式,*表示不限
'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
/*
浏览器的同源策略,就是出于安全考虑,浏览器会限制从脚本发起的跨域HTTP请求(比如异步请求GET, POST, PUT, DELETE, OPTIONS等等),
所以浏览器会向所请求的服务器发起两次请求,第一次是浏览器使用OPTIONS方法发起一个预检请求,第二次才是真正的异步请求,
第一次的预检请求获知服务器是否允许该跨域请求:如果允许,才发起第二次真实的请求;如果不允许,则拦截第二次请求。
Access-Control-Max-Age用来指定本次预检请求的有效期,单位为秒,在此期间不用发出另一条预检请求。
等于0时,表示每次异步请求都发起预检请求
*/
'Access-Control-Max-Age' => 86400,
'Access-Control-Request-Headers' => ['*'],
'Access-Control-Allow-Credentials' => true,
'Access-Control-Expose-Headers' => ['*'],
'Access-Control-Allow-Headers' => ['*']
],
];
//身份验证
$behaviors['authenticator'] = [
'class'=>QueryParamAuth::class,
];
//接口速率限制
$behaviors['rateLimiter'] = [
'class' => RateLimiter::className(),
];
//设置控制器返回数据格式,也可以在config中设置
$behaviors['contentNegotiator'] = [
'class' => ContentNegotiator::className(),
'formats' => [
'application/json' => Response::FORMAT_JSON,
],
];
//设置控制器下,方法的执行权限
//目前很少用到,对于需登录进行的操作,一般在身份认证时,即可阻断,不需要在这里设置
$behaviors['access'] = [
'class' => AccessControl::className(),
'only' => ['index', 'create', 'update'],
'rules' => [
// 允许认证用户
[
'allow' => true,
'roles' => ['@'],
],
// 禁止游客用户执行
[
'allow' => true,
'actions' => ['logout'],
'roles' => ['@'],
],
],
];
//过滤当前控制器下,方法允许的请求方式
$behaviors['verbFilter'] = [
'class' => VerbFilter::className(),
'actions' => [
'index' => [ 'get'], //只允许get方式访问
'create' => [ 'post'], //只允许用post方式访问
'update' => [ 'post']
],
];
return $behaviors;
}
模型行为
public function behaviors() {
return [
//匿名行为,自动添加更新时间戳字段
[
'class' => \yii\behaviors\TimestampBehavior::className(),
'attributes' => [
self::EVENT_BEFORE_INSERT => ['create_time','update_time'],
self::EVENT_BEFORE_UPDATE => ['update_time'],
],
],
];
}
yii\behaviors\AttributeBehavior: 当某些事件发生时, 将指定的值自动分配给 AR 对象的一个或多个属性.
yii\behaviors\TimestampBehavior: 使用当前时间戳自动填充指定的属性.
yii\behaviors\BlameableBehavior: 使用当前用户ID自动填写指定的属性.
yii\behaviors\SluggableBehavior: 自动使用一个可以在 URL 中使用的值来填充指定的属性.
yii\behaviors\AttributeTypecastBehavior: 提供模型属性类型的自动转换功能