Поведения
Поведения — это экземпляры yii\base\Behavior или его дочерних классов. Поведения, также известные как микшины, позволяют расширить функциональность существующего класса компонента, не изменяя его наследование. Прикрепление поведения к компоненту «встраивает» методы и свойства поведения в компонент, делая их доступными, как если бы они были определены в самом классе компонента. Более того, поведение может реагировать на события, генерируемые компонентом, что позволяет поведению также настраивать нормальное выполнение кода компонента.
Определение поведений
Для определения поведения создайте класс, который расширяет 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, с двумя свойствами — prop1 и prop2 — и одним методом foo(). Обратите внимание, что свойство prop2 определяется через геттер getProp2() и сеттер setProp2(). Это связано с тем, что yii\base\Behavior расширяет yii\base\Object и поэтому поддерживает определение свойств через геттеры и сеттеры.
Поскольку этот класс является поведением, при его прикреплении к компоненту, этот компонент также получит свойства prop1 и prop2 и метод foo().
Подсказка: Внутри поведения вы можете получить доступ к компоненту, к которому прикреплено поведение, через свойство yii\base\Behavior::$owner.
Примечание: В случае переопределения метода yii\base\Behavior::__get() и/или yii\base\Behavior::__set() поведения, вам также необходимо переопределить yii\base\Behavior::canGetProperty() и/или yii\base\Behavior::canSetProperty().
Обработка событий компонента
Если поведению необходимо реагировать на события, генерируемые компонентом, к которому оно прикреплено, оно должно переопределить метод 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)
{
// ...
}
}
Метод events() должен возвращать список событий и соответствующих обработчиков. Приведенный выше пример объявляет, что событие EVENT_BEFORE_VALIDATE существует и определяет его обработчик, beforeValidate(). При указании обработчика событий вы можете использовать один из следующих форматов:
- строка, которая ссылается на имя метода класса поведения, как в примере выше
- массив из имени объекта или класса и имени метода в виде строки (без скобок), например,
[$object, 'methodName']; - анонимная функция
Подпись обработчика событий должна быть следующей, где $event относится к параметру события. Дополнительную информацию о событиях см. в разделе События.
function ($event) {
}
Прикрепление поведений
Вы можете прикрепить поведение к компоненту статически или динамически. Первый вариант используется чаще на практике.
Для статического прикрепления поведения переопределите метод behaviors() класса компонента, к которому вы прикрепляете поведение. Метод behaviors() должен возвращать список конфигураций поведения.
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::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',
]
];
}
}
Вы можете ассоциировать имя с поведением, указав ключ массива, соответствующий конфигурации поведения. В этом случае поведение называется именованным поведением. В приведенном выше примере есть два именованных поведения: myBehavior2 и myBehavior4. Если поведению не присвоено имя, оно называется безымянным поведением.
Для динамического прикрепления поведения вызовите метод yii\base\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',
]);
Вы можете прикреплять несколько поведений одновременно, используя метод yii\base\Component::attachBehaviors():
$component->attachBehaviors([
'myBehavior1' => new MyBehavior, // a named behavior
MyBehavior::className(), // an anonymous behavior
]);
Вы также можете прикреплять поведения через конфигурации, как показано ниже:
[
'as myBehavior2' => MyBehavior::className(),
'as myBehavior3' => [
'class' => MyBehavior::className(),
'prop1' => 'value1',
'prop2' => 'value2',
],
]
Дополнительную информацию см. в разделе Конфигурации.
Использование поведений
Чтобы использовать поведение, сначала прикрепите его к компоненту, следуя инструкциям выше. После прикрепления поведения к компоненту, его использование становится простым.
Вы можете получить доступ к общедоступной переменной члена или свойству, определённому геттером и/или сеттером поведения, через компонент, к которому оно прикреплено:
// "prop1" is a property defined in the behavior class echo $component->prop1; $component->prop1 = $value;
Аналогичным образом вы можете вызвать общедоступный метод поведения:
// foo() is a public method defined in the behavior class $component->foo();
Как видите, хотя $component не определяет prop1 и foo(), они могут быть использованы, как если бы они были частью определения компонента, благодаря прикреплённому поведению.
Если два поведения определяют одно и то же свойство или метод и оба прикреплены к одному компоненту, поведение, которое прикреплено к компоненту первым, будет иметь приоритет при доступе к свойству или методу.
Поведение может быть связано с именем при прикреплении к компоненту. В этом случае вы можете получить доступ к объекту поведения, используя имя:
$behavior = $component->getBehavior('myBehavior');
Вы также можете получить все поведения, прикрепленные к компоненту:
$behaviors = $component->getBehaviors();
Открепление поведений
Для открепления поведения вызовите yii\base\Component::detachBehavior() с именем, связанным с поведением:
$component->detachBehavior('myBehavior1');
Вы также можете открепить все поведения:
$component->detachBehaviors();
Использование TimestampBehavior
В заключение, давайте рассмотрим yii\behaviors\TimestampBehavior. Это поведение позволяет автоматически обновлять атрибуты временных меток модели Active Record каждый раз, когда модель сохраняется через метод insert(), update() или save().
Сначала прикрепите это поведение к классу 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'],
],
// if you're using datetime instead of UNIX timestamp:
// 'value' => new Expression('NOW()'),
],
];
}
}
Конфигурация поведения выше указывает, что при:
- вставке записи поведение должно назначить текущую метку времени Unix атрибутам
created_atиupdated_at - обновлении записи поведение должно назначить текущую метку времени Unix атрибуту
updated_at
Примечание: Для работы вышеуказанной реализации с базой данных MySQL, пожалуйста, объявите столбцы (
created_at,updated_at) как int(11) для хранения метки времени Unix.
После добавления этого кода, если у вас есть объект User и вы попытаетесь сохранить его, вы обнаружите, что его атрибуты created_at и updated_at автоматически заполняются текущей меткой времени Unix:
$user = new User; $user->email = 'test@example.com'; $user->save(); echo $user->created_at; // shows the current timestamp
Поведение TimestampBehavior также предлагает полезный метод touch(), который назначит текущую метку времени указанному атрибуту и сохранит его в базе данных:
$user->touch('login_time');
Другие поведения
Существует несколько встроенных и внешних поведений:
- yii\behaviors\BlameableBehavior — автоматически заполняет указанные атрибуты текущим идентификатором пользователя.
- yii\behaviors\SluggableBehavior — автоматически заполняет указанный атрибут значением, которое может быть использовано в качестве «слайса» в URL.
- yii\behaviors\AttributeBehavior — автоматически присваивает указанное значение одному или нескольким атрибутам объекта ActiveRecord при наступлении определённых событий.
- yii2tech\ar\softdelete\SoftDeleteBehavior — предоставляет методы для мягкого удаления и восстановления объектов ActiveRecord, т. е. устанавливает флаг или статус, отмечающий запись как удалённую.
- yii2tech\ar\position\PositionBehavior — позволяет управлять порядком записей в поле целого типа, предоставляя методы переупорядочения.
Сравнение поведений с трейтами
Хотя поведения похожи на трейты тем, что оба «встраивают» свои свойства и методы в основной класс, они различаются по многим аспектам. Как объяснено ниже, у них есть свои плюсы и минусы. Они больше похожи на дополнения друг к другу, чем на альтернативы.
Причины использования поведений
Классы поведения, как и обычные классы, поддерживают наследование. Трейты же можно рассматривать как поддерживаемую языком копипасту. Они не поддерживают наследование.
Поведение может быть прикреплено и откреплено от компонента динамически без необходимости модификации класса компонента. Для использования трейта вам необходимо изменить код класса, который его использует.
Поведения настраиваются, в то время как трейты — нет.
Поведения могут настраивать выполнение кода компонента, реагируя на его события.
При возникновении конфликтов имён между различными поведениями, прикреплёнными к одному компоненту, конфликты автоматически разрешаются путём приоритета поведения, прикреплённого к компоненту в первую очередь. Конфликты имён, вызванные различными трейтами, требуют ручного разрешения путём переименования затронутых свойств или методов.
Причины использования трейтов
Трейты намного эффективнее поведений, так как поведения — это объекты, которые потребляют как время, так и память.
IDE дружелюбнее к трейтам, так как они являются встроенной конструкцией языка.
© 2008–2017 by Yii Software LLC
Licensed under the three clause BSD license.
http://www.yiiframework.com/doc-2.0/guide-concept-behaviors.html