zoukankan      html  css  js  c++  java
  • Yii2的深入学习--行为Behavior

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

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

    定义行为

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

    namespace appcomponents;
    
    use yiiaseBehavior;
    
    class MyBehavior extends Behavior
    {
        public $prop1;
    
        private $_prop2;
    
        public function getProp2()
        {
            return $this->_prop2;
        }
    
        public function setProp2($value)
        {
            $this->_prop2 = $value;
        }
    
        public function foo()
        {
            // ...
        }
    }
    

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

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

    处理事件

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

    namespace appcomponents;
    
    use yiidbActiveRecord;
    use yiiaseBehavior;
    
    class MyBehavior extends Behavior
    {
        // 其它代码
    
        public function events()
        {
            return [
                ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',
            ];
        }
    
        public function beforeValidate($event)
        {
            // 处理器方法逻辑
        }
    }
    

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

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

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

    function ($event) {
    }
    

    附加行为

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

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

    namespace appmodels;
    
    use yiidbActiveRecord;
    use appcomponentsMyBehavior;
    
    class User extends ActiveRecord
    {
        public function behaviors()
        {
            return [
                // 匿名行为,只有行为类名
                MyBehavior::className(),
    
                // 命名行为,只有行为类名
                'myBehavior2' => MyBehavior::className(),
    
                // 匿名行为,配置数组
                [
                    'class' => MyBehavior::className(),
                    'prop1' => 'value1',
                    'prop2' => 'value2',
                ],
    
                // 命名行为,配置数组
                'myBehavior4' => [
                    'class' => MyBehavior::className(),
                    'prop1' => 'value1',
                    'prop2' => 'value2',
                ]
            ];
        }
    }
    

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

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

    use appcomponentsMyBehavior;
    
    // 附加行为对象
    $component->attachBehavior('myBehavior1', new MyBehavior);
    
    // 附加行为类
    $component->attachBehavior('myBehavior2', MyBehavior::className());
    
    // 附加配置数组
    $component->attachBehavior('myBehavior3', [
        'class' => MyBehavior::className(),
        'prop1' => 'value1',
        'prop2' => 'value2',
    ]);
    

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

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

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

    [
        'as myBehavior2' => MyBehavior::className(),
    
        'as myBehavior3' => [
            'class' => MyBehavior::className(),
            'prop1' => 'value1',
            'prop2' => 'value2',
        ],
    ]
    

    使用行为

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

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

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

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

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

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

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

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

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

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

    $behaviors = $component->getBehaviors();
    

    移除行为

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

    $component->detachBehavior('myBehavior1');
    

    也可以移除全部行为:

    $component->detachBehaviors();
    

    使用 TimestampBehavior

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

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

    namespace appmodelsUser;
    
    use yiidbActiveRecord;
    use yiiehaviorsTimestampBehavior;
    
    class User extends ActiveRecord
    {
        // ...
    
        public function behaviors()
        {
            return [
                [
                    'class' => TimestampBehavior::className(),
                    'attributes' => [
                        ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
                        ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],
                    ],
                ],
            ];
        }
    }
    

    以上指定的行为数组:

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

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

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

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

    $user->touch('login_time');
    

    使用 AttributeBehavior

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

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

    namespace appmodelsUser;
    
    use yiidbActiveRecord;
    use yiiehaviorsTimestampBehavior;
    
    class User extends ActiveRecord
    {
        // ...
        public function behaviors()
        {
            return [
                [
                    'class' => AttributeBehavior::className(),
                    'attributes' => [
                        ActiveRecord::EVENT_BEFORE_INSERT => 'attribute1',
                        ActiveRecord::EVENT_BEFORE_UPDATE => 'attribute2',
                    ],
                    'value' => function ($event) {
                        return 'some value';
                    },
                ],
            ];
        }
    }
    

    以上指定的行为数组:

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

    与 PHP traits 的比较

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

    行为的优势

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

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

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

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

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

    traits 的优势

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

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

  • 相关阅读:
    第01组 Beta冲刺(5-5)
    第01组 Beta冲刺(4-5)
    第01组 Beta冲刺(3-5)
    第01组 Beta冲刺(2-5)
    第01组 Beta冲刺(1-5)
    软工实践个人总结
    第03组 每周小结 (3/3)
    第03组 每周小结 (2/3)
    第03组 每周小结 (1/3)
    第03组 Beta冲刺 总结
  • 原文地址:https://www.cnblogs.com/CraryPrimitiveMan/p/5678362.html
Copyright © 2011-2022 走看看