行為(Behaviors)

2018-02-24 15:40 更新

行為

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

定義行為

要定義行為,通過(guò)繼承 yii\base\Behavior 或其子類(lèi)來(lái)建立一個(gè)類(lèi)。如:

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()
    {
        // ...
    }
}

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

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

處理事件

如果要讓行為響應(yīng)對(duì)應(yīng)組件的事件觸發(fā),就應(yīng)覆寫(xiě) 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)指定一個(gè)事件處理器時(shí),要使用以下格式之一:

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

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

function ($event) {
}

附加行為

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

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

namespace app\models;

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

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

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

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

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

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

要?jiǎng)討B(tài)附加行為,在對(duì)應(yīng)組件里調(diào)用 yii\base\Component::attachBehavior() 方法即可,如:

use app\components\MyBehavior;

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

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

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

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

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

還可以通過(guò)配置去附加行為:

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

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

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

使用行為

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

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

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

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

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

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

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

附加行為到組件時(shí)的命名行為,可以使用這個(gè)名稱(chēng)來(lái)訪問(wèn)行為對(duì)象,如下所示:

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

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

$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 的講解來(lái)結(jié)尾,這個(gè)行為支持在 yii\db\ActiveRecord 存儲(chǔ)時(shí)自動(dòng)更新它的時(shí)間戳屬性。

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

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)記錄插入時(shí),行為將當(dāng)前時(shí)間戳賦值給?created_at?和?updated_at?屬性;
  • 當(dāng)記錄更新時(shí),行為將當(dāng)前時(shí)間戳賦值給?updated_at?屬性。

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

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

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

$user->touch('login_time');

與 PHP traits 的比較

盡管行為在 "注入" 屬性和方法到主類(lèi)方面類(lèi)似于?traits?,它們?cè)诤芏喾矫鎱s不相同。如上所述,它們各有利弊。它們更像是互補(bǔ)的而不是相互替代。

行為的優(yōu)勢(shì)

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

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

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

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

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

traits 的優(yōu)勢(shì)

traits 比起行為更高效,因?yàn)樾袨槭菍?duì)象,消耗時(shí)間和內(nèi)存。

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

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

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)