yii2 行为 behavior (2) —— yii2启示录 [ 新手入门 ]
yii2 行为 behavior (1) —— yii2启示录 我们经常用到的两个行为:BlameableBehavior , TimestampBehavior 这两个行为如何起作用的呢? 我们在前面说过:YII2 的行为一定程度上也是对Event 的封装,那行为如何跟事件扯上关系的呢? 且看下面分分析 —— 这里分析 TimestampBehavior 这个行为的执行过程
- 一、静态附加行为
<?php
// common\models\User.php
// class User extends ActiveRecord implements IdentityInterface
...
/**
* @inheritdoc
user 表字段
"create_at" 创建时间
"update_at" 更新时间
绑定 TimestampBehavior 行为后,增加或者更新数据的时候自动给 "create_at"、 "update_at" 赋值
*/
public function behaviors()
{
return [
[
'class' => TimestampBehavior::className(),
'createdAtAttribute' => 'create_at',
'updatedAtAttribute' => 'update_at',
'value' => new Expression('NOW()'),
],
];
}
...
?>
- 二、行为是如何绑定到组件的呢?
执行过程 yii\base\Component::behaviors() yii\base\Component::ensureBehaviors() yii\base\Component::attachBehaviorInternal() yii\base\Behavior::attach()
<?php
// yii2\base\Component.php
...
//1,定义被重载的空壳
public function behaviors()
{
return [];
}
...
...
//2, 确保 behaviors() 中所描述的行为已经绑定到组件
public function ensureBehaviors()
{
if ($this->_behaviors === null) {
$this->_behaviors = [];
foreach ($this->behaviors() as $name => $behavior) {
//遍历$this->behaviors()中的behaviors,并添加到$this->_behaviors数组中
$this->attachBehaviorInternal($name, $behavior);
}
}
}
...
...
//3,
private function attachBehaviorInternal($name, $behavior)
{
if (!($behavior instanceof Behavior)) {
// $behavior 不是 Behavior 实例,只是类名、配置数组,则创建一个$behavior对象
$behavior = Yii::createObject($behavior);
}
// 匿名行为 , 是整数,绑定到组件
if (is_int($name)) {
$behavior->attach($this);
$this->_behaviors[] = $behavior;
} else {
if (isset($this->_behaviors[$name])) {
// 如果有同名的行为存在就先解绑掉
$this->_behaviors[$name]->detach();
}
//再将新的行为绑定上去
$behavior->attach($this);
$this->_behaviors[$name] = $behavior;
}
return $behavior;
}
...
...
// 4 绑定事件
// yii2\base\Behavior.php
public function attach($owner)
{
$this->owner = $owner; // $owner = User , $this = TimestampBehavior
foreach ($this->events() as $event => $handler) {
$owner->on($event, is_string($handler) ? [$this, $handler] : $handler);
}
}
...
?>
- 三、行为是如何相应事件的呢?
(响应事件其实也是触发事件) 其实在绑定行为的时候,[[yii\base\Behavior::attach()]]已经把子弹上膛了 所以剩下的动作就是扣动扳机开枪射击了 如果还没了解YII2的事件机制可以先看看这两篇文章 yii2 事件(1) —— yii2启示录 yii2 事件(2) —— yii2启示录
这个过程经过这几个步骤:
<?php
...
// yii\behaviors\AttributeBehavior::events()
public function events()
{
return array_fill_keys(array_keys($this->attributes),
'evaluateAttributes');
}
...
...
// yii\behaviors\TimeStampBehavior::init()
// class TimestampBehavior extends AttributeBehavior
// TimeStampBehavior 继承自 AttributeBehavior
// TimeStampBehavior 自然也就有了 events()
public function init()
{
parent::init();
if (empty($this->attributes)) {
// 重点看这里
$this->attributes = [
BaseActiveRecord::EVENT_BEFORE_INSERT =>
[$this->createdAtAttribute, $this->updatedAtAttribute],
BaseActiveRecord::EVENT_BEFORE_UPDATE =>
$this->updatedAtAttribute,
];
}
}
...
// 处理完后的 events() , 这是代码处理后的逻辑,原程序无此代码
public function events()
{
return [
BaseActiveRecord::EVENT_BEFORE_INSERT => 'evaluateAttributes',
BaseActiveRecord::EVENT_BEFORE_UPDATE => 'evaluateAttributes',
];
}
...
// yii\base\Behavior::events()
// 定义被重载的空壳
public function events()
{
return [];
}
...
...
// yii2\base\Behavior.php
public function attach($owner)
{
$this->owner = $owner;
// 这里 $owner 是 User
// 遍历上面提到的 events() 所定义的数组
foreach ($this->events() as $event => $handler) {
// 调用 User::on 来绑定事件
// 这里 $handler 为字符串 `evaluateAttributes` ,就是"子弹" —— 我们的事件处理器
// 因此,相当于调用 on(BaseActiveRecord::EVENT_BEFORE_INSERT, [$this, 'evaluateAttributes'])
// $this = TimestampBehavior
$owner->on($event, is_string($handler) ? [$this, $handler] :
$handler);
}
}
...
?>
我们在控制器 AuthorController 里面
<?php
...
$user = new User();
$user->name = 'Qiang';
$user->save(); // a new row is inserted into user table
...
?>
触发了 EVENT_BEFORE_INSERT 和 EVENT_BEFORE_UPDATE
<?php
...
// yii2\db\BaseActiveRecord.php
public function beforeSave($insert)
{
$event = new ModelEvent;
$this->trigger($insert ? self::EVENT_BEFORE_INSERT : self::EVENT_BEFORE_UPDATE, $event);
return $event->isValid;
}
...
/* // yii2\behaviors\AttributeBehavior.php
* [
* 'class' => TimestampBehavior::className(),
* 'createdAtAttribute' => 'create_time',
* 'updatedAtAttribute' => 'update_time',
* 'value' => new Expression('NOW()'),
* ],
*/
public function evaluateAttributes($event)
{
if ($this->skipUpdateOnClean
&& $event->name == ActiveRecord::EVENT_BEFORE_UPDATE
&& empty($this->owner->dirtyAttributes)
) {
return;
}
if (!empty($this->attributes[$event->name])) {
$attributes = (array) $this->attributes[$event->name];
$value = $this->getValue($event); // 这一步自己看代码吧
foreach ($attributes as $attribute) {
// ignore attribute names which are not string (e.g. when set by TimestampBehavior::updatedAtAttribute)
if (is_string($attribute)) {
$this->owner->$attribute = $value; // $User->update_time = time()
}
}
}
}
?>
共 0 条回复
没有找到数据。
qiuxis
注册时间:2016-10-31
最后登录:2017-10-25
在线时长:22小时6分
最后登录:2017-10-25
在线时长:22小时6分
- 粉丝21
- 金钱215
- 威望40
- 积分835