Поведения являются экземплярами 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::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

При наличии этого кода, если у вас есть объект 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 - предоставляет методы для soft-delete и soft-restore ActiveRecord, т. Е. Устанавливает флаг или статус, который помечает запись как удаленную.
  • yii2tech\ar\position\PositionBehavior - позволяет управлять порядком записей в целочисленном поле, предоставляя методы переупорядочения.

Сравнение поведения с чертами

В то время как поведение похоже на черты в том, что они оба «вводят» свои свойства и методы в основной класс, они различаются во многих аспектах. Как объясняется ниже, у обоих есть свои плюсы и минусы. Они скорее дополняют друг друга, чем альтернативы.

Причины использования поведения

  • Классы поведения, как и обычные классы, поддерживают наследование. С другой стороны, черты могут рассматриваться как копия и вставка, поддерживаемая языком. Они не поддерживают наследование.
  • Поведения могут присоединяться и отсоединяться для компонента динамически, не требуя модификации класса компонента. Чтобы использовать признак, вы должны изменить код класса, используя его.
  • Поведение настраивается, а признаки отсутствуют.
  • Поведение может настраивать выполнение кода компонента, реагируя на его события.
  • Когда могут возникать конфликты имен между различными поведениями, связанными с одним компонентом, конфликты автоматически устраняются путем приоритезации поведения, связанного с компонентом в первую очередь. Конфликты имен, вызванные различными признаками, требуют ручного разрешения путем переименования затронутых свойств или методов.

Причины использования признаков

Черты гораздо более эффективны, чем поведение, поскольку поведение - это объекты, которые занимают и время, и память. IDE более дружелюбны к чертам, поскольку они являются конструкцией на родном языке.