位置:首頁 > 框架 > Yii2教學 > 行為

行為

行為

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

定義行為

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

  1. namespace app\\components; 
  2.  
  3. use yii\\base\\Model; 
  4. use yii\\base\\Behavior; 
  5.  
  6. class MyBehavior extends Behavior 
  7.     public $prop1; 
  8.  
  9.     private $_prop2; 
  10.  
  11.     public function getProp2() 
  12.     { 
  13.         return $this->_prop2; 
  14.     } 
  15.  
  16.     public function setProp2($value) 
  17.     { 
  18.         $this->_prop2 = $value; 
  19.     } 
  20.  
  21.     public function foo() 
  22.     { 
  23.         // ... 
  24.     } 
  25. }

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

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

處理事件

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

  1. namespace app\\components; 
  2.  
  3. use yii\\db\\ActiveRecord; 
  4. use yii\\base\\Behavior; 
  5.  
  6. class MyBehavior extends Behavior 
  7.     // 其它代碼 
  8.  
  9.     public function events() 
  10.     { 
  11.         return [ 
  12.             ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate', 
  13.         ]; 
  14.     } 
  15.  
  16.     public function beforeValidate($event) 
  17.     { 
  18.         // 處理器方法邏輯 
  19.     } 
  20. }

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

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

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

  1. function ($event) { 
  2. }

附加行為

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

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

  1. namespace app\\models; 
  2.  
  3. use yii\\db\\ActiveRecord; 
  4. use app\\components\\MyBehavior; 
  5.  
  6. class User extends ActiveRecord 
  7.     public function behaviors() 
  8.     { 
  9.         return [ 
  10.             // 匿名行為,隻有行為類名 
  11.             MyBehavior::className(), 
  12.  
  13.             // 命名行為,隻有行為類名 
  14.             'myBehavior2' => MyBehavior::className(), 
  15.  
  16.             // 匿名行為,配置數組 
  17.             [ 
  18.                 'class' => MyBehavior::className(), 
  19.                 'prop1' => 'value1', 
  20.                 'prop2' => 'value2', 
  21.             ], 
  22.  
  23.             // 命名行為,配置數組 
  24.             'myBehavior4' => [ 
  25.                 'class' => MyBehavior::className(), 
  26.                 'prop1' => 'value1', 
  27.                 'prop2' => 'value2', 
  28.             ] 
  29.         ]; 
  30.     } 
  31. }

通過指定行為配置數組相應的鍵可以給行為關聯一個名稱。這種行為稱為命名行為。上例中,有兩個命名行為:myBehavior2 和 myBehavior4 。如果行為冇有指定名稱就是匿名行為

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

  1. use app\\components\\MyBehavior; 
  2.  
  3. // 附加行為對象 
  4. $component->attachBehavior('myBehavior1', new MyBehavior); 
  5.  
  6. // 附加行為類 
  7. $component->attachBehavior('myBehavior2', MyBehavior::className()); 
  8.  
  9. // 附加配置數組 
  10. $component->attachBehavior('myBehavior3', [ 
  11.     'class' => MyBehavior::className(), 
  12.     'prop1' => 'value1', 
  13.     'prop2' => 'value2', 
  14. ]);

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

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

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

  1.     'as myBehavior2' => MyBehavior::className(), 
  2.  
  3.     'as myBehavior3' => [ 
  4.         'class' => MyBehavior::className(), 
  5.         'prop1' => 'value1', 
  6.         'prop2' => 'value2', 
  7.     ], 
  8. ]

詳情請參考配置章節。

使用行為

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

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

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

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

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

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

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

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

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

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

  1. $behaviors = $component->getBehaviors();

移除行為

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

  1. $component->detachBehavior('myBehavior1');

也可以移除全部行為:

  1. $component->detachBehaviors();

使用 TimestampBehavior

最後以 [[yii\behaviors\TimestampBehavior]] 的講解來結尾,這個行為支持在 [[yii\db\ActiveRecord|Active Record]] 存儲時自動更新它的時間戳屬性。

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

  1. namespace app\\models\\User; 
  2.  
  3. use yii\\db\\ActiveRecord; 
  4. use yii\\behaviors\\TimestampBehavior; 
  5.  
  6. class User extends ActiveRecord 
  7.     // ... 
  8.  
  9.     public function behaviors() 
  10.     { 
  11.         return [ 
  12.             [ 
  13.                 'class' => TimestampBehavior::className(), 
  14.                 'attributes' => [ 
  15.                     ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'], 
  16.                     ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'], 
  17.                 ], 
  18.             ], 
  19.         ]; 
  20.     } 
  21. }

以上指定的行為數組:

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

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

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


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

```php
$user->touch('login_time');

與 PHP traits 的比較

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

行為的優勢

行為類像普通類支持繼承。另一方麵,traits 可以視為 PHP 語言支持的複製粘貼功能,它不支持繼承。

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

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

行為以響應事件來自定義組件的代碼執行。

當不同行為附加到同一組件產生命名衝突時,這個衝突通過先附加行為的優先權自動解決。而由不同 traits 引發的命名衝突需要通過手工重命名衝突屬性或方法來解決。

traits 的優勢

traits 比起行為更高效,因為行為是對象,消耗時間和內存。

IDE 對 traits 更友好,因為它們是語言結構。