Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
yii2
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
PSDI Army
yii2
Commits
48c7e328
Commit
48c7e328
authored
Apr 30, 2014
by
Qiang Xue
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Finished behavior guide [skip ci]
parent
2e892c42
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
254 additions
and
101 deletions
+254
-101
basic-behaviors.md
docs/guide/basic-behaviors.md
+228
-99
basic-configs.md
docs/guide/basic-configs.md
+24
-0
Behavior.php
framework/base/Behavior.php
+2
-2
No files found.
docs/guide/basic-behaviors.md
View file @
48c7e328
Behaviors
=========
A behavior (also knows as
*mixin*
) can be used to enhance the functionality of an existing component without modifying the component's
code. In particular, a behavior can "inject" its public methods and properties into the component, making them directly accessible
via the component itself. A behavior can also respond to events triggered in the component, thus intercepting the normal
code execution. Unlike
[
PHP's traits
](
http://www.php.net/traits
)
, behaviors can be attached to classes at runtime.
Behaviors are instances of
[
[yii\base\Behavior
]
] or its 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 the need of changing its class inheritance.
When a behavior is attached to a component, it will "inject" its methods and properties into the component,
and you can access these methods and properties as if they are defined by the component class. Moreover, a behavior
can respond to the
[
events
](
basic-events.md
)
triggered by the component so that it can customize or adapt the normal
code execution of the component.
Using behaviors
Using Behaviors
---------------
A behavior can be attached to any class that extends from
[
[yii\base\Component
]
] either from code or via application
config
.
To use a behavior, you first need to attach it to a
[
[yii\base\Component|component
]
]. We will describe how to
attach a behavior in the next section
.
### Attaching behaviors via `behaviors` method
Once a behavior is attached to a component, its usage is straightforward.
In order to attach a behavior to a class you can implement the
`behaviors`
method of the component.
As an example, Yii provides the
[
[yii\behaviors\TimestampBehavior
]
] behavior for automatically updating timestamp
fields when saving an
[
[yii\db\ActiveRecord|Active Record
]
] model:
You can access a
*public*
member variable or a
[
property
](
basic-properties.md
)
defined by a getter and/or a setter
of the behavior through the component it is attached to, like the following,
```
php
use
yii\behaviors\TimestampBehavior
;
// "prop1" is a property defined in the behavior class
echo
$component
->
prop1
;
$component
->
prop1
=
$value
;
```
class
User
extends
ActiveRecord
{
// ...
You can also call a
*public*
method of the behavior similarly,
public
function
behaviors
()
{
return
[
'timestamp'
=>
[
'class'
=>
TimestampBehavior
::
className
(),
'attributes'
=>
[
ActiveRecord
::
EVENT_BEFORE_INSERT
=>
[
'created_at'
,
'updated_at'
],
ActiveRecord
::
EVENT_BEFORE_UPDATE
=>
'updated_at'
,
],
],
];
}
}
```
php
// bar() is a public method defined in the behavior class
$component
->
bar
();
```
In the above, the name
`timestamp`
can be used to reference the behavior through the component. For example,
`$user->timestamp`
gives the attached timestamp behavior instance. The corresponding array is the configuration used to create the
[
[yii\behaviors\TimestampBehavior|TimestampBehavior
]
] object.
As you can see, although
`$component`
does not define
`prop1`
and
`bar()`
, they can be used as if they are part
of the component definition.
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 being accessed.
Besides responding to the insertion and update events of ActiveRecord,
`TimestampBehavior`
also provides a method
`touch()`
that can assign the current timestamp to a specified attribute. As aforementioned, you can access this method directly
through the component, like the following:
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, like the following,
```
php
$
user
->
touch
(
'login_time
'
);
$
behavior
=
$component
->
getBehavior
(
'myBehavior
'
);
```
If you do not need to access a behavior object, or the behavior does not need customization, you can also
use the following simplified format when specifying the behavior,
You may also get all behaviors attached to a component:
```
php
use
yii\behaviors\TimestampBehavior
;
$behaviors
=
$component
->
getBehaviors
();
```
Attaching Behaviors
-------------------
You can attach a behavior to a
[
[yii\base\Component|component
]
] either statically or dynamically. The former
is more commonly used in practice.
To attach a behavior statically, override the
[
[yii\base\Component::behaviors()|behaviors()
]
] method of the component
class that it is being attached. For example,
```
php
namespace
app\models
;
use
yii\db\ActiveRecord
;
use
app\components\MyBehavior
;
class
User
extends
ActiveRecord
{
// ...
public
function
behaviors
()
{
return
[
TimestampBehavior
::
className
(),
// or the following if you want to access the behavior object
// 'timestamp' => TimestampBehavior::className(),
// anonymous behavior, behavior class name only
MyBehavior
::
className
(),
// named behavior, behavior class name only
'myBehavior2'
=>
MyBehavior
::
className
(),
// anonymous behavior, configuration array
[
'class'
=>
MyBehavior
::
className
(),
'prop1'
=>
'value1'
,
'prop2'
=>
'value2'
,
],
// named behavior, configuration array
'myBehavior4'
=>
[
'class'
=>
MyBehavior
::
className
(),
'prop1'
=>
'value1'
,
'prop2'
=>
'value2'
,
]
];
}
}
```
### Attaching behaviors dynamically
The
[
[yii\base\Component::behaviors()|behaviors()
]
] method should return a list of behavior
[
configurations
](
basic-configs.md
)
.
Each behavior configuration can be either a behavior class name or a configuration array.
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*
.
Another way to attach a behavior to a component is calling
`attachBehavior`
method like the followig:
To attach a behavior dynamically, call the
[
[yii\base\Component::attachBehavior()
]
] method of the component
that it is attached to. For example,
```
php
$component
=
new
MyComponent
();
$component
->
attachBehavior
();
use
app\components\MyBehavior
;
// attach a behavior object
$component
->
attachBehavior
(
'myBehavior1'
,
new
MyBehavior
);
// attach a behavior class
$component
->
attachBehavior
(
'myBehavior2'
,
MyBehavior
::
className
());
// attach a configuration array
$component
->
attachBehavior
(
'myBehavior3'
,
[
'class'
=>
MyBehavior
::
className
(),
'prop1'
=>
'value1'
,
'prop2'
=>
'value2'
,
]);
```
### Attaching behaviors from config
One can attach a behavior to a component when configuring it with a configuration array. The syntax is like the
following:
Detaching Behaviors
-------------------
To detach a behavior, you can call
[
[yii\base\Component::detachBehavior()
]
] with the name associated with the behavior:
```
php
return
[
// ...
'components'
=>
[
'myComponent'
=>
[
// ...
'as tree'
=>
[
'class'
=>
'Tree'
,
'root'
=>
0
,
],
],
],
];
$component
->
detachBehavior
(
'myBehavior1'
);
```
In the config above
`as tree`
stands for attaching a behavior named
`tree`
, and the array will be passed to
[
[\Yii::createObject()
]
]
to create the behavior object.
You may also detach
*all*
behaviors:
```
php
$component
->
detachBehaviors
();
```
Creating your own behaviors
---------------------------
To create your own behavior, you must define a class that extends
[
[yii\base\Behavior
]
].
Defining Behaviors
------------------
To define a behavior, create a class by extending from
[
[yii\base\Behavior
]
] or its child class. For example,
```
php
namespace
app\components
;
use
yii\base\Model
;
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
()
{
// ...
}
}
```
To make it customizable, like
[
[yii\behaviors\TimestampBehavior
]
], add public properties:
The above code defines the behavior class
`app\components\MyBehavior`
which will provides two properties
`prop1`
and
`prop2`
, and one method
`foo()`
to the component it is attached to. Note that property
`prop2`
is defined via the getter
`getProp2()`
and the setter
`setProp2()`
. This is so because
[
[yii\base\Object
]
]
is an ancestor class of
[
[yii\base\Behavior
]
], which supports defining
[
properties
](
basic-properties.md
)
by getters/setters.
Within a behavior, you can access the component that the behavior is attached to through the
[
[yii\base\Behavior::owner
]
] property.
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
$attr
;
// ...
public
function
events
()
{
return
[
ActiveRecord
::
EVENT_BEFORE_VALIDATE
=>
'beforeValidate'
,
];
}
public
function
beforeValidate
(
$event
)
{
// ...
}
}
```
Now, when the behavior is used, you can set the attribute to which you'd want the behavior to be applied:
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 and
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, 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
](
basic-events.md
)
chapter for more details about events.
```
php
namespace
app\models
;
function
(
$event
)
{
}
```
Using `TimestampBehavior`
-------------------------
To wrap up, let's take a look at
[
[yii\behaviors\TimestampBehavior
]
] - a behavior that supports automatically
updating the timestamp attributes of an
[
[yii\db\ActiveRecord|Active Record
]
] when it is being saved.
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
{
...
...
@@ -148,44 +249,72 @@ class User extends ActiveRecord
public
function
behaviors
()
{
return
[
'mybehavior'
=>
[
'class'
=>
'app\components\MyBehavior'
,
'attr'
=>
'member_type'
[
'class'
=>
TimestampBehavior
::
className
(),
'attributes'
=>
[
ActiveRecord
::
EVENT_BEFORE_INSERT
=>
[
'created_at'
,
'updated_at'
],
ActiveRecord
::
EVENT_BEFORE_UPDATE
=>
[
'updated_at'
],
],
],
];
}
}
```
Behaviors are normally written to take action when certain events occur. Below we're implementing
`events`
method
to assign event handlers:
The behavior configuration above specifies that
*
when the record is being inserted, the behavior should assign the current timestamp to
the
`created_at`
and
`updated_at`
attributes;
*
when the record is being updated, the behavior should assign the current timestamp to the
`updated_at`
attribute.
Now 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 timestamp:
```
php
namespace
app\components
;
$user
=
new
User
;
$user
->
email
=
'test@example.com'
;
$user
->
save
();
echo
$user
->
created_at
;
// shows the current timestamp
```
use
yii\base\Behavior
;
use
yii\db\ActiveRecord
;
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:
class
MyBehavior
extends
Behavior
{
public
$attr
;
```
php
$user
->
touch
(
'login_time'
);
```
public
function
events
()
{
return
[
ActiveRecord
::
EVENT_BEFORE_INSERT
=>
'beforeInsert'
,
ActiveRecord
::
EVENT_BEFORE_UPDATE
=>
'beforeUpdate'
,
];
}
public
function
beforeInsert
()
{
$model
=
$this
->
owner
;
// Use $model->$attr
}
Comparison with Traits
----------------------
While behaviors are similar to
[
traits
](
http://www.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 rather than replacements to each other.
### Pros for Behaviors
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 you to modify the component class.
To use a trait, you must modify 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 is name conflict among different behaviors attached to the same component, the conflict is
automatically resolved by respecting the behavior that is attached to the component first.
Name conflict caused by different traits requires you to manually resolve it by renaming the affected
properties or methods.
### Pros for Traits
Traits are much more efficient than behaviors because behaviors are objects which take both time and memory.
IDEs are more friendly to traits as they are language construct.
public
function
beforeUpdate
()
{
$model
=
$this
->
owner
;
// Use $model->$attr
}
}
```
docs/guide/basic-configs.md
View file @
48c7e328
...
...
@@ -178,3 +178,27 @@ return [
],
];
```
### Attaching behaviors from config
One can attach a behavior to a component when configuring it with a configuration array. The syntax is like the
following:
```
php
return
[
// ...
'components'
=>
[
'myComponent'
=>
[
// ...
'as tree'
=>
[
'class'
=>
'Tree'
,
'root'
=>
0
,
],
],
],
];
```
In the config above
`as tree`
stands for attaching a behavior named
`tree`
, and the array will be passed to
[
[\Yii::createObject()
]
]
to create the behavior object.
framework/base/Behavior.php
View file @
48c7e328
...
...
@@ -46,8 +46,8 @@ class Behavior extends \yii\base\Object
*
* ~~~
* [
* Model::EVENT_BEFORE_VALIDATE => 'myBeforeValidate',
* Model::EVENT_AFTER_VALIDATE => 'myAfterValidate',
*
Model::EVENT_BEFORE_VALIDATE => 'myBeforeValidate',
*
Model::EVENT_AFTER_VALIDATE => 'myAfterValidate',
* ]
* ~~~
*
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment