mirror of
				https://github.com/yiisoft/yii2.git
				synced 2025-11-01 03:26:36 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			361 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			361 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| Behaviors
 | |
| =========
 | |
| 
 | |
| Behaviors are instances of [[yii\base\Behavior]], or of a child class. Behaviors, also known
 | |
| as [mixins](http://en.wikipedia.org/wiki/Mixin), allow you to enhance the functionality
 | |
| of an existing [[yii\base\Component|component]] class without needing to change the class's inheritance.
 | |
| Attaching a behavior to a component "injects" the behavior's methods and properties into the component, making those methods and properties accessible as if they were defined in the component class itself. Moreover, a behavior
 | |
| can respond to the [events](concept-events.md) triggered by the component, which allows behaviors to also customize the normal
 | |
| code execution of the component.
 | |
| 
 | |
| 
 | |
| Defining Behaviors <span id="defining-behaviors"></span>
 | |
| ------------------
 | |
| 
 | |
| To define a behavior, create a class that extends [[yii\base\Behavior]], or extends a child class. For example:
 | |
| 
 | |
| ```php
 | |
| 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()
 | |
|     {
 | |
|         // ...
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| The above code defines the behavior class `app\components\MyBehavior`, with two properties
 | |
| `prop1` and `prop2` and one method `foo()`. Note that property `prop2`
 | |
| is defined via the getter `getProp2()` and the setter `setProp2()`. This is the case because [[yii\base\Behavior]] extends [[yii\base\BaseObject]] and therefore supports defining [properties](concept-properties.md) via getters and setters.
 | |
| 
 | |
| Because this class is a behavior, when it is attached to a component, that component will then also have the `prop1` and `prop2` properties and the `foo()` method.
 | |
| 
 | |
| > Tip: Within a behavior, you can access the component that the behavior is attached to through the [[yii\base\Behavior::owner]] property.
 | |
| 
 | |
| > Note: In case [[yii\base\Behavior::__get()]] and/or [[yii\base\Behavior::__set()]] method of behavior is overridden you
 | |
| need to override [[yii\base\Behavior::canGetProperty()]] and/or [[yii\base\Behavior::canSetProperty()]] as well.
 | |
| 
 | |
| Handling Component Events
 | |
| ------------------
 | |
| 
 | |
| If a behavior needs to respond to the events triggered by the component it is attached to, it should override the
 | |
| [[yii\base\Behavior::events()]] method. For example:
 | |
| 
 | |
| ```php
 | |
| 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)
 | |
|     {
 | |
|         // ...
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| The [[yii\base\Behavior::events()|events()]] method should return a list of events and their corresponding handlers.
 | |
| The above example declares that the [[yii\db\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]] event exists and defines
 | |
| its handler, `beforeValidate()`. When specifying an event handler, you may use one of the following formats:
 | |
| 
 | |
| * a string that refers to the name of a method of the behavior class, like the example above
 | |
| * an array of an object or class name, and a method name as a string (without parentheses), e.g., `[$object, 'methodName']`;
 | |
| * an anonymous function
 | |
| 
 | |
| The signature of an event handler should be as follows, where `$event` refers to the event parameter. Please refer
 | |
| to the [Events](concept-events.md) section for more details about events.
 | |
| 
 | |
| ```php
 | |
| function ($event) {
 | |
| }
 | |
| ```
 | |
| 
 | |
| Attaching Behaviors <span id="attaching-behaviors"></span>
 | |
| -------------------
 | |
| 
 | |
| You can attach a behavior to a [[yii\base\Component|component]] either statically or dynamically. The former is more common in practice.
 | |
| 
 | |
| To attach a behavior statically, override the [[yii\base\Component::behaviors()|behaviors()]] method of the component
 | |
| class to which the behavior is being attached. The [[yii\base\Component::behaviors()|behaviors()]] method should return a list of behavior [configurations](concept-configurations.md).
 | |
| Each behavior configuration can be either a behavior class name or a configuration array:
 | |
| 
 | |
| ```php
 | |
| namespace app\models;
 | |
| 
 | |
| use yii\db\ActiveRecord;
 | |
| use app\components\MyBehavior;
 | |
| 
 | |
| class User extends ActiveRecord
 | |
| {
 | |
|     public function behaviors()
 | |
|     {
 | |
|         return [
 | |
|             // anonymous behavior, behavior class name only
 | |
|             MyBehavior::class,
 | |
| 
 | |
|             // named behavior, behavior class name only
 | |
|             'myBehavior2' => MyBehavior::class,
 | |
| 
 | |
|             // anonymous behavior, configuration array
 | |
|             [
 | |
|                 'class' => MyBehavior::class,
 | |
|                 'prop1' => 'value1',
 | |
|                 'prop2' => 'value2',
 | |
|             ],
 | |
| 
 | |
|             // named behavior, configuration array
 | |
|             'myBehavior4' => [
 | |
|                 'class' => MyBehavior::class,
 | |
|                 'prop1' => 'value1',
 | |
|                 'prop2' => 'value2',
 | |
|             ]
 | |
|         ];
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| You may associate a name with a behavior by specifying the array key corresponding to the behavior configuration. In this case, the behavior is called a *named behavior*. In the above example, there are two named behaviors:
 | |
| `myBehavior2` and `myBehavior4`. If a behavior is not associated with a name, it is called an *anonymous behavior*.
 | |
| 
 | |
| 
 | |
| To attach a behavior dynamically, call the [[yii\base\Component::attachBehavior()]] method of the component to which the behavior is being attached:
 | |
| 
 | |
| ```php
 | |
| use app\components\MyBehavior;
 | |
| 
 | |
| // attach a behavior object
 | |
| $component->attachBehavior('myBehavior1', new MyBehavior());
 | |
| 
 | |
| // attach a behavior class
 | |
| $component->attachBehavior('myBehavior2', MyBehavior::class);
 | |
| 
 | |
| // attach a configuration array
 | |
| $component->attachBehavior('myBehavior3', [
 | |
|     'class' => MyBehavior::class,
 | |
|     'prop1' => 'value1',
 | |
|     'prop2' => 'value2',
 | |
| ]);
 | |
| ```
 | |
| 
 | |
| You may attach multiple behaviors at once using the [[yii\base\Component::attachBehaviors()]] method:
 | |
| 
 | |
| ```php
 | |
| $component->attachBehaviors([
 | |
|     'myBehavior1' => new MyBehavior(), // a named behavior
 | |
|     MyBehavior::class,                 // an anonymous behavior
 | |
| ]);
 | |
| ```
 | |
| 
 | |
| You may also attach behaviors through [configurations](concept-configurations.md) like the following: 
 | |
| 
 | |
| ```php
 | |
| [
 | |
|     'as myBehavior2' => MyBehavior::class,
 | |
| 
 | |
|     'as myBehavior3' => [
 | |
|         'class' => MyBehavior::class,
 | |
|         'prop1' => 'value1',
 | |
|         'prop2' => 'value2',
 | |
|     ],
 | |
| ]
 | |
| ```
 | |
| 
 | |
| For more details,
 | |
| please refer to the [Configurations](concept-configurations.md#configuration-format) section.
 | |
| 
 | |
| Using Behaviors <span id="using-behaviors"></span>
 | |
| ---------------
 | |
| 
 | |
| To use a behavior, first attach it to a [[yii\base\Component|component]] per the instructions above. Once a behavior is attached to a component, its usage is straightforward.
 | |
| 
 | |
| You can access a *public* member variable or a [property](concept-properties.md) defined by a getter and/or a setter
 | |
| of the behavior through the component it is attached to:
 | |
| 
 | |
| ```php
 | |
| // "prop1" is a property defined in the behavior class
 | |
| echo $component->prop1;
 | |
| $component->prop1 = $value;
 | |
| ```
 | |
| 
 | |
| You can also call a *public* method of the behavior similarly:
 | |
| 
 | |
| ```php
 | |
| // foo() is a public method defined in the behavior class
 | |
| $component->foo();
 | |
| ```
 | |
| 
 | |
| As you can see, although `$component` does not define `prop1` and `foo()`, they can be used as if they are part
 | |
| of the component definition due to the attached behavior.
 | |
| 
 | |
| If two behaviors define the same property or method and they are both attached to the same component,
 | |
| the behavior that is attached to the component *first* will take precedence when the property or method is accessed.
 | |
| 
 | |
| A behavior may be associated with a name when it is attached to a component. If this is the case, you may
 | |
| access the behavior object using the name:
 | |
| 
 | |
| ```php
 | |
| $behavior = $component->getBehavior('myBehavior');
 | |
| ```
 | |
| 
 | |
| You may also get all behaviors attached to a component:
 | |
| 
 | |
| ```php
 | |
| $behaviors = $component->getBehaviors();
 | |
| ```
 | |
| 
 | |
| 
 | |
| Detaching Behaviors <span id="detaching-behaviors"></span>
 | |
| -------------------
 | |
| 
 | |
| To detach a behavior, call [[yii\base\Component::detachBehavior()]] with the name associated with the behavior:
 | |
| 
 | |
| ```php
 | |
| $component->detachBehavior('myBehavior1');
 | |
| ```
 | |
| 
 | |
| You may also detach *all* behaviors:
 | |
| 
 | |
| ```php
 | |
| $component->detachBehaviors();
 | |
| ```
 | |
| 
 | |
| 
 | |
| Using `TimestampBehavior` <span id="using-timestamp-behavior"></span>
 | |
| -------------------------
 | |
| 
 | |
| To wrap up, let's take a look at [[yii\behaviors\TimestampBehavior]]. This behavior supports automatically
 | |
| updating the timestamp attributes of an [[yii\db\ActiveRecord|Active Record]] model anytime the model is saved via
 | |
| `insert()`, `update()` or `save()` method.
 | |
| 
 | |
| First, attach this behavior to the [[yii\db\ActiveRecord|Active Record]] class that you plan to use:
 | |
| 
 | |
| ```php
 | |
| namespace app\models\User;
 | |
| 
 | |
| use yii\db\ActiveRecord;
 | |
| use yii\behaviors\TimestampBehavior;
 | |
| 
 | |
| class User extends ActiveRecord
 | |
| {
 | |
|     // ...
 | |
| 
 | |
|     public function behaviors()
 | |
|     {
 | |
|         return [
 | |
|             [
 | |
|                 'class' => TimestampBehavior::class,
 | |
|                 'attributes' => [
 | |
|                     ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
 | |
|                     ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],
 | |
|                 ],
 | |
|                 // if you're using datetime instead of UNIX timestamp:
 | |
|                 // 'value' => new Expression('NOW()'),
 | |
|             ],
 | |
|         ];
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| The behavior configuration above specifies that when the record is being:
 | |
| 
 | |
| * inserted, the behavior should assign the current UNIX timestamp to
 | |
|   the `created_at` and `updated_at` attributes
 | |
| * updated, the behavior should assign the current UNIX timestamp to the `updated_at` attribute
 | |
| 
 | |
| > Note: For the above implementation to work with MySQL database, please declare the columns(`created_at`, `updated_at`) as int(11) for being UNIX timestamp.
 | |
| 
 | |
| With that code in place, if you have a `User` object and try to save it, you will find its `created_at` and `updated_at` are automatically
 | |
| filled with the current UNIX timestamp:
 | |
| 
 | |
| ```php
 | |
| $user = new User;
 | |
| $user->email = 'test@example.com';
 | |
| $user->save();
 | |
| echo $user->created_at;  // shows the current timestamp
 | |
| ```
 | |
| 
 | |
| The [[yii\behaviors\TimestampBehavior|TimestampBehavior]] also offers a useful method
 | |
| [[yii\behaviors\TimestampBehavior::touch()|touch()]], which will assign the current timestamp
 | |
| to a specified attribute and save it to the database:
 | |
| 
 | |
| ```php
 | |
| $user->touch('login_time');
 | |
| ```
 | |
| 
 | |
| Other behaviors
 | |
| ---------------
 | |
| 
 | |
| There are several built-in and external behaviors available:
 | |
| 
 | |
| - [[yii\behaviors\BlameableBehavior]] - automatically fills the specified attributes with the current user ID.
 | |
| - [[yii\behaviors\SluggableBehavior]] - automatically fills the specified attribute with a value that can be used
 | |
|   as a slug in a URL.
 | |
| - [[yii\behaviors\AttributeBehavior]] - automatically assigns a specified value to one or multiple attributes of
 | |
|   an ActiveRecord object when certain events happen.
 | |
| - [yii2tech\ar\softdelete\SoftDeleteBehavior](https://github.com/yii2tech/ar-softdelete) - provides methods to soft-delete
 | |
|   and soft-restore ActiveRecord i.e. set flag or status which marks record as deleted.
 | |
| - [yii2tech\ar\position\PositionBehavior](https://github.com/yii2tech/ar-position) - allows managing records order in an
 | |
|   integer field by providing reordering methods.
 | |
| 
 | |
| Comparing Behaviors with Traits <span id="comparison-with-traits"></span>
 | |
| ----------------------
 | |
| 
 | |
| While behaviors are similar to [traits](https://secure.php.net/traits) in that they both "inject" their
 | |
| properties and methods to the primary class, they differ in many aspects. As explained below, they
 | |
| both have pros and cons. They are more like complements to each other rather than alternatives.
 | |
| 
 | |
| 
 | |
| ### Reasons to Use Behaviors <span id="pros-for-behaviors"></span>
 | |
| 
 | |
| Behavior classes, like normal classes, support inheritance. Traits, on the other hand,
 | |
| can be considered as language-supported copy and paste. They do not support inheritance.
 | |
| 
 | |
| Behaviors can be attached and detached to a component dynamically without requiring modification of the component class.
 | |
| To use a trait, you must modify the code of the class using it.
 | |
| 
 | |
| Behaviors are configurable while traits are not.
 | |
| 
 | |
| Behaviors can customize the code execution of a component by responding to its events.
 | |
| 
 | |
| When there can be name conflicts among different behaviors attached to the same component, the conflicts are
 | |
| automatically resolved by prioritizing the behavior attached to the component first.
 | |
| Name conflicts caused by different traits requires manual resolution by renaming the affected
 | |
| properties or methods.
 | |
| 
 | |
| 
 | |
| ### Reasons to Use Traits <span id="pros-for-traits"></span>
 | |
| 
 | |
| Traits are much more efficient than behaviors as behaviors are objects that take both time and memory.
 | |
| 
 | |
| IDEs are more friendly to traits as they are a native language construct.
 | |
| 
 | 
