Когда вы пишете на Yii2, рано или поздно упираетесь в момент, когда нужно добавить функциональность сразу нескольким моделям или контроллерам. Можно, конечно, скопировать код, но это путь в никуда. На помощь приходят события и поведенческие примеси (behaviors). Они позволяют расширять классы без изменения их кода. Звучит сложно? На самом деле, это как конструктор LEGO: добавляете нужные блоки — и всё работает.
Что такое события и зачем они нужны
События — это способ сказать: «Когда произойдёт вот это, сделай то». В Yii2 события реализованы на уровне компонента, а так как почти всё в Yii2 — компоненты, то события доступны везде: в моделях, контроллерах, компонентах.
Представьте, что у вас есть интернет-магазин. Когда пользователь оформляет заказ, вы хотите отправить ему письмо, обновить склад и начислить бонусы. Вместо того чтобы писать весь этот код в методе `createOrder()`, вы можете просто вызвать событие `order_created` и подписаться на него в трёх разных местах. Код становится чище, а добавить новое действие (например, отправить уведомление в Telegram) можно вообще не трогая логику заказа.
// В модели Order
class Order extends \yii\db\ActiveRecord
{
const EVENT_ORDER_CREATED = 'order_created';
public function createOrder($data)
{
// ... логика сохранения ...
$this->trigger(self::EVENT_ORDER_CREATED);
}
}
А вот так подписываемся на событие:
// В контроллере или где-то ещё
$order = new Order();
$order->on(Order::EVENT_ORDER_CREATED, function($event) {
// Отправляем письмо
Yii::$app->mailer->compose('order-created', ['order' => $event->sender])
->setTo($event->sender->email)
->send();
});
В обработчик события передаётся объект `yii\base\Event`, у которого есть свойство `sender` — это объект, который вызвал событие. Удобно, правда?
Поведенческие примеси: что это и как упрощают жизнь
Поведенческие примеси (behaviors) — это способ добавить методы и свойства в класс без наследования. В Yii2 поведение — это объект, который прикрепляется к компоненту и «вливает» в него свои методы. Классический пример — поведение `TimestampBehavior`, которое автоматически заполняет поля `created_at` и `updated_at`.
Допустим, у вас есть модель `Post`. Вы хотите, чтобы при сохранении автоматически проставлялась дата создания. Вместо того чтобы писать это в каждом методе, вы просто подключаете поведение:
use yii\behaviors\TimestampBehavior;
class Post extends \yii\db\ActiveRecord
{
public function behaviors()
{
return [
TimestampBehavior::className(),
];
}
}
Всё. Теперь при сохранении модели поля `created_at` и `updated_at` заполнятся автоматически. Поведение можно настроить под себя, например, указать, какие поля заполнять:
public function behaviors()
{
return [
[
'class' => TimestampBehavior::className(),
'createdAtAttribute' => 'created_at',
'updatedAtAttribute' => 'updated_at',
'value' => function() { return date('Y-m-d H:i:s'); },
],
];
}
Поведения — это не только про время. Вы можете написать своё поведение, которое добавляет, например, методы для работы с тегами или рейтингами. Это позволяет переиспользовать код между разными моделями.
Создаём своё поведение
Давайте напишем простое поведение, которое автоматически генерирует slug (человеко-понятный URL) для модели. Например, для новостей или статей.
namespace app\components;
use yii\base\Behavior;
use yii\db\ActiveRecord;
use yii\helpers\Inflector;
class SlugBehavior extends Behavior
{
public $sourceAttribute = 'title';
public $slugAttribute = 'slug';
public function events()
{
return [
ActiveRecord::EVENT_BEFORE_VALIDATE => 'generateSlug',
];
}
public function generateSlug($event)
{
$model = $this->owner;
if (empty($model->{$this->slugAttribute})) {
$model->{$this->slugAttribute} = Inflector::slug($model->{$this->sourceAttribute});
}
}
}
Теперь подключаем его к модели:
class Post extends \yii\db\ActiveRecord
{
public function behaviors()
{
return [
[
'class' => SlugBehavior::className(),
'sourceAttribute' => 'title',
'slugAttribute' => 'slug',
],
];
}
}
Теперь при создании записи, если slug не заполнен, он сгенерируется автоматически из заголовка. Обратите внимание, что мы используем событие `EVENT_BEFORE_VALIDATE` — оно срабатывает перед проверкой данных. Это удобно, потому что мы можем быть уверены, что slug будет готов к моменту сохранения.
События в поведении: как они работают
Поведения могут не только добавлять методы, но и реагировать на события владельца. В методе `events()` мы возвращаем массив, где ключ — имя события, а значение — метод поведения, который будет вызван.
Например, если мы хотим логировать все изменения модели, мы можем создать поведение, которое подписывается на событие `EVENT_AFTER_UPDATE`:
class LogBehavior extends Behavior
{
public function events()
{
return [
ActiveRecord::EVENT_AFTER_UPDATE => 'logUpdate',
];
}
public function logUpdate($event)
{
// Логируем изменения
Yii::info('Модель обновлена: ' . $this->owner->className(), 'model');
}
}
И прикрепляем его к модели:
public function behaviors()
{
return [
LogBehavior::className(),
];
}
Теперь при каждом обновлении модели в лог будет писаться сообщение. Круто, что мы не трогаем код самой модели — просто добавляем поведение.
Практические советы и лайфхаки
Давайте подведём итоги и дадим несколько советов, которые пригодятся в реальной работе.
Вот ещё несколько полезных приёмов:
- Не злоупотребляйте событиями. Если событие вызывает цепочку из десяти обработчиков, это может запутать. Старайтесь, чтобы обработчиков было немного и они были логичными.
- Используйте поведения для переиспользования кода. Если вы замечаете, что одна и та же логика повторяется в нескольких моделях, — вынесите её в поведение.
- События можно вызывать цепочкой. Вы можете вызвать событие внутри обработчика другого события, но будьте осторожны — легко получить бесконечный цикл.
- Проверяйте, есть ли уже готовые поведения. В Yii2 есть много встроенных поведений: `TimestampBehavior`, `BlameableBehavior`, `SluggableBehavior` (в расширениях). Прежде чем писать своё, поищите готовое.
Сравним, когда что использовать:
| Критерий | События | Поведения |
|---|---|---|
| Что делают | Реагируют на действия | Добавляют функциональность |
| Когда использовать | Когда нужно выполнить дополнительные действия при событии | Когда нужно добавить методы или свойства классу |
| Пример | Отправка письма после создания заказа | Автозаполнение даты создания |
Ещё один лайфхак: если вы используете события в своих моделях, обязательно документируйте их. Имена событий лучше выносить в константы класса, чтобы не ошибиться при подписке.
Что в итоге
События и поведенческие примеси — это мощные инструменты Yii2, которые помогают писать чистый, модульный и легко расширяемый код. События позволяют развязать логику, а поведения — переиспользовать функциональность. Освоив их, вы значительно ускорите разработку и упростите поддержку проектов.
Не бойтесь экспериментировать: создайте своё первое поведение или событие — и вы почувствуете, как код становится гибче. А когда освоитесь, сможете строить сложные системы без боли и страданий.
Удачи в кодинге!



