Если вы когда-нибудь писали на Yii2, то наверняка замечали, что иногда нужно сделать так, чтобы при наступлении какого-то действия (например, сохранения модели или входа пользователя) автоматически выполнялись другие действия. Классический подход — написать код прямо в методе или в контроллере. Но это быстро превращается в кашу: что-то забыли, что-то переписали, а потом всё падает. Выход — события. Они позволяют разнести логику по разным местам и не перегружать основные методы. В этой статье я расскажу, как работать с событиями в Yii2, покажу примеры и дам советы, которые пригодятся даже опытным разработчикам.

Digital-студия WNDER

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

Представьте, что ваше приложение — это кухня. Вы — повар, а события — это звонки на кухне: "Заказ готов!", "Продукты закончились!", "Клиент передумал!". Вместо того чтобы постоянно проверять, не готов ли суп, вы просто вешаете колокольчик, и когда суп готов — он звенит. Вы слышите и идёте действовать. В программировании это работает так же: объект (например, модель) может в нужный момент "позвонить" — вызвать событие. Другие части приложения могут "подписаться" на это событие и выполнить свои действия.

В Yii2 события встроены в базовый класс yii\base\Component. Практически всё, что вы используете — модели, контроллеры, компоненты — наследуется от него. Значит, у вас уже есть доступ к событиям без дополнительных танцев с бубном.

Как создать и вызвать событие

Создать событие в Yii2 — это просто объявить константу с именем события и метод, который его инициирует. Например, пусть у нас есть модель Order (заказ). Мы хотим, чтобы после сохранения заказа происходило событие EVENT_ORDER_CREATED.

namespace app\models;

use yii\db\ActiveRecord;

class Order extends ActiveRecord
{
    const EVENT_ORDER_CREATED = 'order-created';

    public function afterSave($insert, $changedAttributes)
    {
        parent::afterSave($insert, $changedAttributes);
        if ($insert) {
            $this->trigger(self::EVENT_ORDER_CREATED);
        }
    }
}

Метод trigger — это "колокольчик". Он вызывает всех, кто подписан на событие. Здесь мы вызываем его только при вставке (создании), а не при обновлении.

Правило: Называйте события с использованием префикса EVENT_ и в нижнем регистре. Это стандарт Yii2, так проще читать код.

Как подписаться на событие

Теперь, когда событие есть, нужно на него подписаться. Это делается через метод on(). Можно подписаться в любом месте: в контроллере, в поведении, в конфигурационном файле.

Первый способ — прямо в коде, например, в контроллере:

use app\models\Order;

$order = new Order();

// Подписываемся
$order->on(Order::EVENT_ORDER_CREATED, function($event) {
    // Отправляем письмо администратору
    Yii::$app->mailer->compose('new-order', ['order' => $event->sender])
        ->setTo('admin@example.com')
        ->send();
});

$order->save(); // Триггер события сработает

Второй способ — через конфигурацию приложения. Это удобно, если вы хотите, чтобы обработчик вызывался всегда, для всех экземпляров модели. Например, в config/main.php:

'components' => [
    'order' => [
        'class' => 'app\models\Order',
        'on order-created' => function($event) {
            // Логируем создание заказа
            Yii::info('Заказ создан: ' . $event->sender->id, 'order');
        },
    ],
]

Но так обычно не делают для моделей, потому что модели создаются не всегда через компонент. Лучше использовать поведение (behavior). Поведение — это отдельный класс, который может прикрепляться к компоненту и перехватывать его события. Это очень мощный инструмент.

Поведения и события: идеальная пара

Поведения в Yii2 — это способ расширить функциональность класса без наследования. Они отлично работают с событиями: вы можете в поведении подписаться на события владельца. Создадим поведение, которое при создании заказа отправляет письмо.

namespace app\behaviors;

use yii\base\Behavior;
use app\models\Order;

class OrderNotifierBehavior extends Behavior
{
    public function events()
    {
        return [
            Order::EVENT_ORDER_CREATED => 'sendNotification',
        ];
    }

    public function sendNotification($event)
    {
        // $event->sender — это модель Order
        Yii::$app->mailer->compose('new-order', ['order' => $event->sender])
            ->setTo('admin@example.com')
            ->send();
    }
}

Теперь прикрепим поведение к модели:

use yii\behaviors\TimestampBehavior;
use app\behaviors\OrderNotifierBehavior;

class Order extends ActiveRecord
{
    public function behaviors()
    {
        return [
            TimestampBehavior::className(),
            OrderNotifierBehavior::className(),
        ];
    }
}

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

События с данными: передаём параметры

Иногда нужно передать в обработчик дополнительные данные. Для этого используется класс yii\base\Event. Вы можете создать свой класс события, расширяющий его, и добавить туда свойства.

namespace app\events;

use yii\base\Event;

class OrderCreatedEvent extends Event
{
    public $order;
    public $notifyUser = true;
}

Теперь в модели вызываем событие с этим классом:

use app\events\OrderCreatedEvent;

class Order extends ActiveRecord
{
    const EVENT_ORDER_CREATED = 'order-created';

    public function afterSave($insert, $changedAttributes)
    {
        parent::afterSave($insert, $changedAttributes);
        if ($insert) {
            $event = new OrderCreatedEvent(['order' => $this]);
            $this->trigger(self::EVENT_ORDER_CREATED, $event);
        }
    }
}

А в обработчике мы уже можем получить доступ к этим данным:

$order->on(Order::EVENT_ORDER_CREATED, function($event) {
    // $event->order — это модель
    // $event->notifyUser — флаг
    if ($event->notifyUser) {
        // отправить письмо пользователю
    }
});

Это очень удобно, когда нужно передать контекст.

Практические советы и лайфхаки

Вот несколько моментов, которые я вынес из своего опыта:

  • Не злоупотребляйте событиями. Если логика простая и не планируется расширение — не городите огород. События нужны для слабосвязанных систем.
  • Используйте события для аудита. Логирование действий, отправка уведомлений, синхронизация с внешними сервисами — классические кейсы.
  • Не забывайте отписываться. Если вы подписались через on() и объект долго живёт, обработчик может вызваться неожиданно. Но в Yii2 это редко проблема, так как объекты обычно живут недолго.
  • Проверяйте, есть ли обработчики. Иногда полезно знать, есть ли кто-то на событии. Используйте hasEventHandlers().
if ($this->hasEventHandlers(Order::EVENT_ORDER_CREATED)) {
    // что-то делаем
}

Также стоит знать, что события можно использовать не только в моделях, но и в компонентах, контроллерах и даже в представлениях (хотя там реже).

Таблица: методы работы с событиями

Метод Описание Пример
on() Подписка на событие $obj->on('event', handler)
off() Отписка от события $obj->off('event', handler)
trigger() Вызов события $obj->trigger('event')
hasEventHandlers() Проверка наличия обработчиков $obj->hasEventHandlers('event')

Итог

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

Если вы хотите, чтобы ваш проект был написан грамотно и с душой — обращайтесь в Digital-студию WNDER. Мы знаем, как превратить скучный код в произведение искусства.

Удачи в разработке!

Студия WNDER