События - это механизм, внедряющий элементы собственного кода в существующий код в определенные моменты его исполнения. К событию можно присоединить собственный код, который будет выполняться автоматически при срабатывании события. Например, объект, отвечающий за почту, может инициировать событие messageSent при успешной отправке сообщения. При этом если нужно отслеживать успешно отправленные сообщения, достаточно присоединить соответствующий код к событию messageSent.
Для реализации механизма событий нужен:
- Код события - константа, например EVENT_HELLO
- Нужен обработчик события. Код который выполнится при срабатывании события. Обработчик нужно навесить с помощью команды on
- Событие нужно запустить с помощью метода trigger.
Для работы с событиями Yii использует базовый класс yii\base\Component. Если класс должен инициировать
Все прекреплённые обработчики хранятся в массиве $events класса Component. Методы on и off хранятся в классе Component - он содержит работу с событиями и поведениями
- В Yii2 используются события навешенные на ActiveRecord такие как - ActiveRecord::EVENT_AFTER_INSERT это событие срабатывают при вставке записи в БД автоматически. Нет нужды заботиться о том чтобы его запустить.
В обработчике можно получить доступ к объекту, который инициировал событие, с помощью свойства $event->sender.
Константы событий ActiveRecord находятся в :
abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
{
/**
* @event Event an event that is triggered when the record is initialized via [[init()]].
*/
const EVENT_INIT = 'init';
/**
* @event Event an event that is triggered after the record is created and populated with query result.
*/
const EVENT_AFTER_FIND = 'afterFind';
/**
* @event ModelEvent an event that is triggered before inserting a record.
* You may set [[ModelEvent::isValid]] to be `false` to stop the insertion.
*/
const EVENT_BEFORE_INSERT = 'beforeInsert';
/**
* @event AfterSaveEvent an event that is triggered after a record is inserted.
*/
const EVENT_AFTER_INSERT = 'afterInsert';
/**
* @event ModelEvent an event that is triggered before updating a record.
* You may set [[ModelEvent::isValid]] to be `false` to stop the update.
*/
const EVENT_BEFORE_UPDATE = 'beforeUpdate';
/**
* @event AfterSaveEvent an event that is triggered after a record is updated.
*/
const EVENT_AFTER_UPDATE = 'afterUpdate';
/**
* @event ModelEvent an event that is triggered before deleting a record.
* You may set [[ModelEvent::isValid]] to be `false` to stop the deletion.
*/
const EVENT_BEFORE_DELETE = 'beforeDelete';
/**
* @event Event an event that is triggered after a record is deleted.
*/
const EVENT_AFTER_DELETE = 'afterDelete';
/**
* @event Event an event that is triggered after a record is refreshed.
* @since 2.0.8
*/
const EVENT_AFTER_REFRESH = 'afterRefresh';
.....................
}
Реализация в контроллере:
$article->on(ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
$followers = ['[email protected]',
'[email protected]',
'[email protected]'
];
foreach ($followers as $follower) {
Yii::$app->mailer->compose()
->setFrom('[email protected]')
->setTo($follower)
->setSubject($event->sender->name)
->setTextBody($event->sender->description)
->send();
}
\yii\helpers\VarDumper::dump('Email sent successfuly!', 10, true);
});
if (!$article->save()) {
echo VarDumper::dumpAsString($article->getErrors());
};
Как видно здесь нет trigger. Событие срабатывает автоматически при вставки (сохранении) записи в БД в таблице article
По факту Если мы подвесим обработчик на ActiveRecord::EVENT_AFTER_INSERT сработает метод afterSave() в классе BaseActiveRecord.
-
В любой модели мы можем имплементировать и видоизменить метод beforeSave($insert) и afterSave($insert) их реализация находится в класса BAseActiveRecord
public function beforeSave($insert) { $event = new ModelEvent(); $this->trigger($insert ? self::EVENT_BEFORE_INSERT : self::EVENT_BEFORE_UPDATE, $event);
return $event->isValid; }
которые запустят обработчик события автоматически. но это касается модели.
Необходимо не забыть о том что нужен
parent::beforeSave($insert);
Иначе метод beforeSave внутри модели перезапишет всю реализацию .
-
Так же в модели можно описать собственный обработчик
const EVENT_OUR_CUSTOM_EVENT = 'eventOurCustomEvent';
Но в этом случае обязательно его задействовать нужно:
if ($article->save()) {
$article->trigger(Article::EVENT_OUR_CUSTOM_EVENT);
}
-
В LoginForm методе login() компонента \yii\web\User
можно также навесить обработчик на событиеclass User extends Component { const EVENT_BEFORE_LOGIN = 'beforeLogin'; const EVENT_AFTER_LOGIN = 'afterLogin'; const EVENT_BEFORE_LOGOUT = 'beforeLogout'; const EVENT_AFTER_LOGOUT = 'afterLogout';
для этого в конфигурационном файле можно прописать:
$config = [
.........
'components' => [
'user' => [
'identityClass' => 'app\models\User',
'enableAutoLogin' => true,
'on afterLogin' => function (\yii\web\UserEvent $event) {
$user = $event->identity;
$user -> updateAttributes(['logged_at' => time()]);
},
],
.........
здесь также можно вместо анонимной функции использовать статический метод класса
namespase app\models;
class User extends ActiveRecord {
public static function updateLastLogin (\yii\web\UserEvent $event) {
$user = $event->identity;
$user -> updateAttributes(['logged_at' => time()]);
},
}
'on afterLogin' => ['\app\models\User', 'updateLastLogin']
-
Навесить обработчик на событие Application в контроллере нельзя потому что приложение запускается раньше контроллера. Для того чтобы навесить на приложение нужно навесить обработчик в конфигурационном файле или в index.php методом run
abstract class Application extends Module { /** * @event Event an event raised before the application starts to handle a request. */ const EVENT_BEFORE_REQUEST = 'beforeRequest'; /** * @event Event an event raised after the application successfully handles a request * (before the response is sent out). */ const EVENT_AFTER_REQUEST = 'afterRequest'; } $config = [ ......... 'components' => [ ], 'on beforeRequest' => function ($event) {.....}, ];
-
В классе Event добавлены статические методы on. off, trigger. Это сделано чтобы навешивать события по имени класса. Навесить событие на все экземпляры класса ActiveRecord.
Event::on(ActiveRecord::class, ActiveRecord::EVENT_AFTER_INSERT, function ($event) { Yii::debug(get_class($event->sender) . ' добавлен'); });
Обработчик будет вызван при срабатывании события EVENT_AFTER_INSERT в экземплярах класса ActiveRecord или его потомков. В обработчике можно получить доступ к объекту, который инициировал событие, с помощью свойства $event->sender. Т.е. после вставке записи в ЛЮБУЮ модель нажего сайта.
При срабатывании события будут в первую очередь вызваны обработчики на уровне экземпляра, а затем - обработчики на уровне класса.
-
Сделаем систему оповещений для нашего итернет магазина. После вставки записи в таблицу-модель заказов Order мы запустим обработчик orderCreated(), при вводе поситителем записи в поле оставить отзыв о товаре т.е. в таблицу Opinion мы запустим обработчик opinionCreated().
nsmespaxe app\components;
use yii\base\BootstrapInterface;
class ShopNotificator implements BootstrapInterface { public function bootstrap ($app) { Event::on( Order::className(), ActiveRacord::EVENT_AFTER_INSERT, [$this, 'orderCreated'] ); Event::on( Opinion::className(), ActiveRacord::EVENT_AFTER_INSERT, [$this, 'opinionCreated'] );
}public function orderCreated(Event $event) { //Отправляем увежомление о заказе администратору и прадовцу } public function opinionCreated(Event $event) { //Отправляем увежомление об отзыве администратору и прадовцу }
}
$config = [ 'id' => 'app', 'bootstrap' => [ 'log', '\app\components\ShopNotificator', ] ];