qiuxis 2016-12-11 12:35:41 364次浏览 0条回复 0 0 0

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()
			}
		}
	}
}

?>

yii2 行为(3) —— yii2启示录

    没有找到数据。
您需要登录后才可以回复。登录 | 立即注册
在线
396