FAQ по Yii2

  1. 1. Компонеты
  2. 2. Свойства
  3. 3. События
  4. 4. Поведения
    1. 4.1. Прикрепление поведений
    2. 4.2. Использование поведения TimestampBehavior
  5. 5. Плюсы поведения по сравнению с трейтами
  6. 6. Плюсы трейтов

Компонеты

Компоненты — это главные строительные блоки приложений основанных на Yii. Компоненты наследуются от класса yii\base\Component или его наследников.

Три главные возможности, которые компоненты предоставляют для других классов:

  • Свойства
  • События
  • Поведения

Если ваши компоненты не нуждаются в этих двух возможностях, вам стоит унаследовать их от yii\base\Object, а не от yii\base\Component.
Поступив так, вы сделаете ваши компоненты такими же эффективными, как и обычные PHP объекты, но с поддержкой свойств.

1
2
3
4
5
6
7
8
9
use yii\jui\DatePicker;
echo DatePicker::widget([
'language' => 'ru',
'name' => 'country',
'clientOptions' => [
'dateFormat' => 'yy-mm-dd',
],
]);

При наследовании ваших классов от yii\base\Component или yii\base\Object, рекомендуется следовать некоторым соглашениям:

  • Если вы переопределяете конструктор, то добавьте последним аргументом параметр $config и затем передайте его в конструктор предка.
  • Всегда вызывайте конструктор предка в конце вашего переопределенного конструктора.
  • Если вы переопределяете метод yii\base\Object::init(), убедитесь, что вы вызываете родительскую реализацию этого метода в начале вашего метода init().

Свойства

Геттер — это метод, чьё название начинается со слова get. Имя сеттера начинается со слова set. Часть названия после get или set определяет имя свойства. Например, геттер getLabel() и/или сеттер setLabel() определяют свойство label, как показано в коде ниже:

Обычный вызов property_exists() не работает для магических свойств. Для них необходимо использовать canGetProperty() или canSetProperty().

События

События - это механизм, внедряющий элементы собственного кода в существующий код в определенные моменты его исполнения. К событию можно присоединить собственный код, который будет выполняться автоматически при срабатывании события.

Например, объект, отвечающий за почту, может инициировать событие messageSent при успешной отправке сообщения. При этом если нужно отслеживать успешно отправленные сообщения, достаточно присоединить соответствующий код к событию messageSent.

Обработчик события - это callback-функция PHP, которая выполняется при срабатывании события, к которому она присоединена.
Можно использовать следующие callback-функции:

  • глобальную функцию PHP, указав строку с именем функции (без скобок), например, ‘trim’;
  • метод объекта, указав массив, содержащий строки с именами объекта и метода (без скобок), например, [$object, ‘methodName’];
  • статический метод класса, указав массив, содержащий строки с именами класса и метода (без скобок), например, [‘ClassName’, ‘methodName’];
  • анонимную функцию, например, function ($event) { … }.

Через параметр $event обработчик события может получить следующую информацию о возникшем событии:

  • event name
  • event sender: объект, метод trigger() которого был вызван
  • custom data: данные, которые были предоставлены во время присоединения обработчика события (будет описано ниже)

Обработчики события присоединяются с помощью метода yii\base\Component::on().

Чтобы запретить в обработчике вызов всех следующих за ним обработчиков, необходимо установить свойство yii\base\Event::$handled

1
2
3
$foo->on(Foo::EVENT_HELLO, function ($event) {
$event->handled = true;
});

События инициируются при вызове метода yii\base\Component::trigger().

Для отсоединения обработчика от события используется метод yii\base\Component::off().

Например, объект Active Record инициирует событие EVENT_AFTER_INSERT после добавления в базу данных новой записи. Чтобы отслеживать записи, добавленные в базу данных каждым объектом Active Record, можно использовать следующий код:

1
2
3
4
5
6
7
use Yii;
use yii\base\Event;
use yii\db\ActiveRecord;
Event::on(ActiveRecord::className(), ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
Yii::trace(get_class($event->sender) . ' добавлен');
});
  • Достаточно присоединить обработчик на уровне класса, вызвав статический метод yii\base\Event::on().
  • Отсоединить обработчик события на уровне класса можно с помощью метода yii\base\Event::off().

События на уровни интерфейсов

1
2
3
4
namespace app\interfaces;
interface DanceEventInterface
{ const EVENT_DANCE = 'dance'; }

1
2
3
Event::on('app\interfaces\DanceEventInterface', DanceEventInterface::EVENT_DANCE, function ($event) {
Yii::trace(get_class($event->sender).'just danced');
});

Yii поддерживает так называемые глобальные события, которые на самом деле основаны на нестандартном использовании описанного выше механизма событий. Для глобальных событий нужен глобально доступный объект-синглтон, например, экземпляр приложения - application.

Поведения

Поведения (behaviors) — это экземпляры класса yii\base\Behavior или класса, унаследованного от него. Поведения, также известные как примеси, позволяют расширять функциональность существующих компонентов без необходимости изменения дерева наследования.
После прикрепления поведения к компоненту, его методы и свойства “внедряются” в компонент, и становятся доступными так же, как если бы они были объявлены в самом классе компонента.

Поведения создаются путем расширения базового класса yii\base\Behavior

Если поведению требуется реагировать на события компонента, к которому оно прикреплено, то необходимо переопределить метод yii\base\Behavior::events().

1
2
3
4
5
6
7
8
9
10
11
public function events()
{
return [
ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',
];
}
public function beforeValidate($event)
{
// ...
}

Прикрепление поведений

Прикрепить поведение к компоненту можно как статически, так и динамически. На практике чаще используется статическое прикрепление.

Для того чтобы прикрепить поведение статически, необходимо переопределить метод behaviors() компонента, к которому его планируется прикрепить.
Метод behaviors() должен возвращать список конфигураций поведений. Конфигурация поведения представляет собой имя класса поведения, либо массив его настроек:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class User extends ActiveRecord
{
public function behaviors()
{
return [
// анонимное поведение, прикрепленное по имени класса
MyBehavior::className(),
// именованное поведение, прикрепленное по имени класса
'myBehavior2' => MyBehavior::className(),
// анонимное поведение, сконфигурированное с использованием массива
[
'class' => MyBehavior::className(),
'prop1' => 'value1',
'prop2' => 'value2',
],
// именованное поведение, сконфигурированное с использованием массива
'myBehavior4' => [
'class' => MyBehavior::className(),
'prop1' => 'value1',
'prop2' => 'value2',
]
];
}
}

Для того, чтобы прикрепить поведение динамически, необходимо вызвать метод yii\base\Component::attachBehavior() требуемого компонента:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// прикрепляем объект поведения
$component->attachBehavior('myBehavior1', new MyBehavior);
// прикрепляем по имени класса поведения
$component->attachBehavior('myBehavior2', MyBehavior::className());
// прикрепляем используя массив конфигураций
$component->attachBehavior('myBehavior3', [
'class' => MyBehavior::className(),
'prop1' => 'value1',
'prop2' => 'value2',
]);
**Чтобы отвязать поведение от компонента, необходимо вызвать метод yii\base\Component::detachBehavior(), указав имя, связанное с поведением:**

Использование поведения TimestampBehavior

В заключении, давайте посмотрим на yii\behaviors\TimestampBehavior — поведение, которое позволяет автоматически обновлять атрибуты с метками времени при сохранении Active Record моделей через insert(), update() или save().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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'],
],
// если вместо метки времени UNIX используется datetime:
// 'value' => new Expression('NOW()'),
],
];
}
}

Плюсы поведения по сравнению с трейтами

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

Плюсы трейтов

  • Трейты являются гораздо более производительными, чем поведения, которые, являясь объектами, требуют дополнительного времени и памяти.