Yii2的深入學習--行為Behavior


我們先來看下行為在 Yii2 中的使用,如下內容摘自 Yii2中文文檔

行為是 [[yii\base\Behavior]] 或其子類的實例。行為,也稱為 mixins,可以無須改變類繼承關系即可增強一個已有的 [[yii\base\Component|組件]] 類功能。當行為附加到組件后,它將“注入”它的方法和屬性到組件,然后可以像訪問組件內定義的方法和屬性一樣訪問它們。此外,行為通過組件能響應被觸發的事件,從而自定義或調整組件正常執行的代碼。

定義行為

要定義行為,通過繼承 [[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 並為要附加行為的組件提供了兩個屬性 prop1prop2 和一個方法 foo() 。注意屬性 prop2 是通過 getter getProp2() 和 setter setProp2() 定義的。能這樣用是因為 [[yii\base\Object]] 是 [[yii\base\Behavior]] 的祖先類,此祖先類支持用 getter 和 setter 方法定義屬性

Tip: 在行為內部可以通過 [[yii\base\Behavior::owner]] 屬性訪問行為已附加的組件。

處理事件

如果要讓行為響應對應組件的事件觸發,就應覆寫 [[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()|events()]] 方法返回事件列表和相應的處理器。上例聲明了 [[yii\db\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]] 事件和它的處理器 beforeValidate() 。當指定一個事件處理器時,要使用以下格式之一:

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

處理器的格式如下,其中 $event 指向事件參數。關於事件的更多細節請參考事件

function ($event) {
}

附加行為

可以靜態或動態地附加行為到[[yii\base\Component|組件]]。前者在實踐中更常見。

要靜態附加行為,覆寫行為要附加的組件類的 [[yii\base\Component::behaviors()|behaviors()]] 方法即可。[[yii\base\Component::behaviors()|behaviors()]] 方法應該返回行為配置列表。每個行為配置可以是行為類名也可以是配置數組。如:

namespace app\models;

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

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 。如果行為沒有指定名稱就是匿名行為

要動態附加行為,在對應組件里調用 [[yii\base\Component::attachBehavior()]] 方法即可,如:

use app\components\MyBehavior;

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

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

// 附加配置數組
$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',
    ],
]

使用行為

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

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

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

類似地也可以調用行為的公共方法:

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

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

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

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

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

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

$behaviors = $component->getBehaviors();

移除行為

要移除行為,可以調用 [[yii\base\Component::detachBehavior()]] 方法用行為相關聯的名字實現:

$component->detachBehavior('myBehavior1');

也可以移除全部行為:

$component->detachBehaviors();

使用 TimestampBehavior

這個行為支持在 [[yii\db\ActiveRecord|Active Record]] 存儲時自動更新它的時間戳屬性。

首先,附加這個行為到計划使用該行為的 [[yii\db\ActiveRecord|Active Record]] 類:

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

以上指定的行為數組:

  • 當記錄插入時,行為將當前時間戳賦值給 created_atupdated_at 屬性;
  • 當記錄更新時,行為將當前時間戳賦值給 updated_at 屬性。

保存 User 對象,將會發現它的 created_atupdated_at 屬性自動填充了當前時間戳:

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

[[yii\behaviors\TimestampBehavior|TimestampBehavior]] 行為還提供了一個有用的方法 [[yii\behaviors\TimestampBehavior::touch()|touch()]],這個方法能將當前時間戳賦值給指定屬性並保存到數據庫:

$user->touch('login_time');

使用 AttributeBehavior

這個行為支持在 [[yii\db\ActiveRecord|Active Record]] 事件觸發時自動修改它的屬性。

首先,附加這個行為到計划使用該行為的 [[yii\db\ActiveRecord|Active Record]] 類:

namespace app\models\User;

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

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 更友好,因為它們是語言結構。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM