События и поведения — это два мощных инструмента Yii2, которые позволяют писать чистый, расширяемый код без дублирования. Если вы ещё не используете их на полную, эта статья для вас. Мы разберёмся, как они работают, и посмотрим примеры, которые можно сразу применить в проекте.

Digital-студия WNDER

Что такое события и зачем они нужны

Представьте, что вы заказали пиццу. Курьер звонит в дверь — это событие. Вы открываете дверь и забираете пиццу — это обработчик события. В программировании событие — это сигнал о том, что что-то произошло. 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. А потом переходите к более сложным сценариям. Главное — не бойтесь экспериментировать.

В итоге вы получите код, который легче поддерживать и расширять. А это — половина успеха в разработке.

Студия WNDER