События и поведения — это два мощных инструмента Yii2, которые позволяют писать чистый, расширяемый код без дублирования. Если вы ещё не используете их на полную, эта статья для вас. Мы разберёмся, как они работают, и посмотрим примеры, которые можно сразу применить в проекте.
Что такое события и зачем они нужны
Представьте, что вы заказали пиццу. Курьер звонит в дверь — это событие. Вы открываете дверь и забираете пиццу — это обработчик события. В программировании событие — это сигнал о том, что что-то произошло. Yii2 позволяет «подписываться» на такие сигналы и выполнять свой код.
Зачем это нужно? Чтобы не менять класс, если требуется добавить новое действие. Например, после сохранения модели нужно отправить email. Вместо того чтобы лезть в метод save(), вы просто вешаете обработчик на событие afterSave.
В Yii2 события делятся на два типа:
- События уровня класса — срабатывают для всех экземпляров класса.
- События уровня экземпляра — только для конкретного объекта.
Чаще всего используются события ActiveRecord: beforeSave, afterSave, beforeDelete, afterDelete и другие.
Как подписаться на событие
Самый простой способ — использовать метод on() у объекта. Допустим, у нас есть модель Post, и мы хотим после сохранения записать в лог заголовок поста.
$post = new Post();
$post->on(Post::EVENT_AFTER_SAVE, function ($event) {
Yii::info('Сохранён пост: ' . $event->sender->title, 'post');
});
$post->title = 'Привет, мир!';
$post->save();
Обратите внимание: в обработчик передаётся объект события $event, у которого есть свойство sender — это сама модель.
Если нужно подписаться на событие для всех экземпляров класса, используйте статический метод Event::on():
use yii\base\Event;
Event::on(Post::class, Post::EVENT_AFTER_SAVE, function ($event) {
Yii::info('Сохранён пост: ' . $event->sender->title, 'post');
});
Такой обработчик сработает для любого поста, который будет сохранён после этой строки.
Event::on(). Если только для конкретного объекта — $object->on().
Поведения (behaviors) — что это и с чем едят
Поведения — это способ добавить функциональность классу без наследования. По сути, это «примеси» (mixins). Вы можете «подмешать» к модели методы и свойства, как будто они определены в самом классе.
В Yii2 поведения реализованы через класс yii\base\Behavior. Чтобы использовать поведение, нужно переопределить метод behaviors() в модели.
Классический пример — TimestampBehavior, который автоматически заполняет поля created_at и updated_at.
use yii\behaviors\TimestampBehavior;
public function behaviors()
{
return [
[
'class' => TimestampBehavior::class,
'attributes' => [
ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],
],
'value' => new \yii\db\Expression('NOW()'),
],
];
}
Теперь при создании или обновлении записи поля будут заполняться автоматически. Без поведения пришлось бы писать это в beforeSave().
Поведения могут иметь свои события и обработчики. Например, SluggableBehavior генерирует slug из заголовка и записывает его в указанное поле.
Вот таблица с популярными поведениями из коробки:
| Поведение | Класс | Что делает |
|---|---|---|
| Timestamp | yii\behaviors\TimestampBehavior |
Заполняет даты создания и обновления |
| Sluggable | yii\behaviors\SluggableBehavior |
Генерирует URL-совместимый slug |
| Blameable | yii\behaviors\BlameableBehavior |
Записывает ID пользователя, создавшего/изменившего запись |
Как создать своё поведение
Иногда встроенных поведений не хватает. Написать своё — проще простого. Нужно унаследоваться от yii\base\Behavior и реализовать нужные методы.
Допустим, мы хотим добавить модели метод isPublished(), который проверяет, опубликована ли запись. Создадим поведение PublishBehavior.
namespace app\behaviors;
use yii\base\Behavior;
class PublishBehavior extends Behavior
{
public $publishedAttribute = 'published_at';
public function isPublished()
{
return $this->owner->{$this->publishedAttribute} !== null;
}
}
Теперь подключим его к модели Post:
public function behaviors()
{
return [
PublishBehavior::class,
];
}
После этого можно вызывать $post->isPublished(), как будто метод определён в самой модели.
Обратите внимание: внутри поведения $this->owner — это объект, к которому прикреплено поведение.
events() поведения. Например, можно автоматически устанавливать дату публикации при сохранении.
События в поведениях: продвинутый уровень
Поведения могут не только добавлять методы, но и подписываться на события «хозяина». Для этого переопределите метод events().
Допустим, мы хотим, чтобы при сохранении модели автоматически проставлялась дата публикации, если она не указана. Добавим в PublishBehavior:
public function events()
{
return [
ActiveRecord::EVENT_BEFORE_INSERT => 'setPublishedAt',
ActiveRecord::EVENT_BEFORE_UPDATE => 'setPublishedAt',
];
}
public function setPublishedAt($event)
{
if ($this->owner->{$this->publishedAttribute} === null) {
$this->owner->{$this->publishedAttribute} = date('Y-m-d H:i:s');
}
}
Теперь при создании или обновлении записи, если поле published_at пустое, оно заполнится текущей датой. И всё это без изменения кода модели.
Такой подход очень удобен: логика инкапсулирована в поведении, и её легко переиспользовать в других моделях.
Сравнение событий и поведений
Когда использовать события, а когда поведения? Вот краткая шпаргалка:
| Критерий | События | Поведения |
|---|---|---|
| Цель | Реакция на действие | Добавление функциональности |
| Где размещать код | В обработчике | В классе поведения |
| Переиспользование | Сложнее | Легко |
| Влияние на класс | Не меняет структуру | Добавляет методы и свойства |
На практике их часто комбинируют: поведение подписывается на события и выполняет свою логику.
Практические советы
- Не пихайте всё в одно поведение — делайте их маленькими и специализированными.
- Используйте
Event::on()для глобальных обработчиков, но не злоупотребляйте — это может усложнить отладку. - В поведениях всегда указывайте
ownerчерез$this->owner, а не через$this->getOwner()— короче и читаемее. - Если поведение должно быть настраиваемым, добавляйте публичные свойства.
- Помните, что поведения могут конфликтовать, если определяют одинаковые методы. Yii2 выбросит исключение.
Yii::$app->on('afterAction', ...) или ставьте логи в обработчиках. Так вы увидите, что и когда срабатывает.
Заключение
События и поведения — это не просто «фишки» Yii2, а реальные инструменты для построения гибкой архитектуры. Они позволяют добавлять функциональность без изменения существующего кода, что особенно важно в больших проектах.
Начните с малого: подключите TimestampBehavior к своим моделям, попробуйте написать простое поведение для генерации slug. А потом переходите к более сложным сценариям. Главное — не бойтесь экспериментировать.
В итоге вы получите код, который легче поддерживать и расширять. А это — половина успеха в разработке.



