行為(Behaviors)

2018-02-24 15:40 更新

行為

行為是 yii\base\Behavior 或其子類的實(shí)例。行為,也稱為?mixins,可以無須改變類繼承關(guān)系即可增強(qiáng)一個已有的 yii\base\Component 類功能。當(dāng)行為附加到組件后,它將“注入”它的方法和屬性到組件,然后可以像訪問組件內(nèi)定義的方法和屬性一樣訪問它們。此外,行為通過組件能響應(yīng)被觸發(fā)的事件,從而自定義或調(diào)整組件正常執(zhí)行的代碼。

定義行為

要定義行為,通過繼承 yii\base\Behavior 或其子類來建立一個類。如:

namespace app\components;

use yii\base\Behavior;

class MyBehavior extends Behavior
{
    public $prop1;

    private $_prop2;

    public function getProp2()
    {
        return $this->_prop2;
    }

    public function setProp2($value)
    {
        $this->_prop2 = $value;
    }

    public function foo()
    {
        // ...
    }
}

以上代碼定義了行為類?app\components\MyBehavior?并為要附加行為的組件提供了兩個屬性?prop1?、?prop2?和一個方法?foo()。注意屬性?prop2?是通過 getter?getProp2()?和 setter?setProp2()?定義的。能這樣用是因?yàn)?yii\base\Object 是 yii\base\Behavior 的祖先類,此祖先類支持用 getter 和 setter 方法定義屬性

提示:在行為內(nèi)部可以通過 yii\base\Behavior::owner 屬性訪問行為已附加的組件。

處理事件

如果要讓行為響應(yīng)對應(yīng)組件的事件觸發(fā),就應(yīng)覆寫 yii\base\Behavior::events() 方法,如:

namespace app\components;

use yii\db\ActiveRecord;
use yii\base\Behavior;

class MyBehavior extends Behavior
{
    // 其它代碼

    public function events()
    {
        return [
            ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',
        ];
    }

    public function beforeValidate($event)
    {
        // 處理器方法邏輯
    }
}

yii\base\Behavior::events() 方法返回事件列表和相應(yīng)的處理器。上例聲明了 yii\db\ActiveRecord::EVENT_BEFORE_VALIDATE 事件和它的處理器?beforeValidate()?。當(dāng)指定一個事件處理器時,要使用以下格式之一:

  • 指向行為類的方法名的字符串,如上例所示;
  • 對象或類名和方法名的數(shù)組,如?[$object, 'methodName'];
  • 匿名方法。

處理器的格式如下,其中?$event?指向事件參數(shù)。關(guān)于事件的更多細(xì)節(jié)請參考事件

function ($event) {
}

附加行為

可以靜態(tài)或動態(tài)地附加行為到y(tǒng)ii\base\Component。前者在實(shí)踐中更常見。

要靜態(tài)附加行為,覆寫行為要附加的組件類的 yii\base\Component::behaviors() 方法即可。yii\base\Component::behaviors() 方法應(yīng)該返回行為配置列表。每個行為配置可以是行為類名也可以是配置數(shù)組。如:

namespace app\models;

use yii\db\ActiveRecord;
use app\components\MyBehavior;

class User extends ActiveRecord
{
    public function behaviors()
    {
        return [
            // 匿名行為,只有行為類名
            MyBehavior::className(),

            // 命名行為,只有行為類名
            'myBehavior2' => MyBehavior::className(),

            // 匿名行為,配置數(shù)組
            [
                'class' => MyBehavior::className(),
                'prop1' => 'value1',
                'prop2' => 'value2',
            ],

            // 命名行為,配置數(shù)組
            'myBehavior4' => [
                'class' => MyBehavior::className(),
                'prop1' => 'value1',
                'prop2' => 'value2',
            ]
        ];
    }
}

通過指定行為配置數(shù)組相應(yīng)的鍵可以給行為關(guān)聯(lián)一個名稱。這種行為稱為命名行為。上例中,有兩個命名行為:myBehavior2?和myBehavior4?。如果行為沒有指定名稱就是匿名行為

要動態(tài)附加行為,在對應(yīng)組件里調(diào)用 yii\base\Component::attachBehavior() 方法即可,如:

use app\components\MyBehavior;

// 附加行為對象
$component->attachBehavior('myBehavior1', new MyBehavior);

// 附加行為類
$component->attachBehavior('myBehavior2', MyBehavior::className());

// 附加配置數(shù)組
$component->attachBehavior('myBehavior3', [
    'class' => MyBehavior::className(),
    'prop1' => 'value1',
    'prop2' => 'value2',
]);

可以通過 yii\base\Component::attachBehaviors() 方法一次附加多個行為:

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

還可以通過配置去附加行為:

[
    'as myBehavior2' => MyBehavior::className(),

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

詳情請參考配置章節(jié)。

使用行為

使用行為,必須像前文描述的一樣先把它附加到 yii\base\Component 類或其子類。一旦行為附加到組件,就可以直接使用它。

行為附加到組件后,可以通過組件訪問一個行為的公共成員變量或 getter 和 setter 方法定義的屬性

// "prop1" 是定義在行為類的屬性
echo $component->prop1;
$component->prop1 = $value;

類似地也可以調(diào)用行為的公共方法:

// foo() 是定義在行為類的公共方法
$component->foo();

如你所見,盡管?$component?未定義?prop1?和?foo()?,它們用起來也像組件自己定義的一樣。

如果兩個行為都定義了一樣的屬性或方法,并且它們都附加到同一個組件,那么首先附加上的行為在屬性或方法被訪問時有優(yōu)先權(quán)。

附加行為到組件時的命名行為,可以使用這個名稱來訪問行為對象,如下所示:

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

也能獲取附加到這個組件的所有行為:

$behaviors = $component->getBehaviors();

移除行為

要移除行為,可以調(diào)用 yii\base\Component::detachBehavior() 方法用行為相關(guān)聯(lián)的名字實(shí)現(xiàn):

$component->detachBehavior('myBehavior1');

也可以移除全部行為:

$component->detachBehaviors();

使用?TimestampBehavior

最后以 yii\behaviors\TimestampBehavior 的講解來結(jié)尾,這個行為支持在 yii\db\ActiveRecord 存儲時自動更新它的時間戳屬性。

首先,附加這個行為到計(jì)劃使用該行為的 yii\db\ActiveRecord 類:

namespace app\models\User;

use yii\db\ActiveRecord;
use yii\behaviors\TimestampBehavior;

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'],
                ],
            ],
        ];
    }
}

以上指定的行為數(shù)組:

  • 當(dāng)記錄插入時,行為將當(dāng)前時間戳賦值給?created_at?和?updated_at?屬性;
  • 當(dāng)記錄更新時,行為將當(dāng)前時間戳賦值給?updated_at?屬性。

保存?User?對象,將會發(fā)現(xiàn)它的?created_at?和?updated_at?屬性自動填充了當(dāng)前時間戳:

$user = new User;
$user->email = 'test@example.com';
$user->save();
echo $user->created_at;  // 顯示當(dāng)前時間戳

yii\behaviors\TimestampBehavior 行為還提供了一個有用的方法 yii\behaviors\TimestampBehavior::touch(),這個方法能將當(dāng)前時間戳賦值給指定屬性并保存到數(shù)據(jù)庫:

$user->touch('login_time');

與 PHP traits 的比較

盡管行為在 "注入" 屬性和方法到主類方面類似于?traits?,它們在很多方面卻不相同。如上所述,它們各有利弊。它們更像是互補(bǔ)的而不是相互替代。

行為的優(yōu)勢

行為類像普通類支持繼承。另一方面,traits 可以視為 PHP 語言支持的復(fù)制粘貼功能,它不支持繼承。

行為無須修改組件類就可動態(tài)附加到組件或移除。要使用 traits,必須修改使用它的類。

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

行為以響應(yīng)事件來自定義組件的代碼執(zhí)行。

當(dāng)不同行為附加到同一組件產(chǎn)生命名沖突時,這個沖突通過先附加行為的優(yōu)先權(quán)自動解決。而由不同 traits 引發(fā)的命名沖突需要通過手工重命名沖突屬性或方法來解決。

traits 的優(yōu)勢

traits 比起行為更高效,因?yàn)樾袨槭菍ο螅臅r間和內(nèi)存。

IDE 對 traits 更友好,因?yàn)樗鼈兪钦Z言結(jié)構(gòu)。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號