How to Use Events in Yii2

How events work in Yii2

You may create init() method in model:

public function init()
{
$this->on(Event::ACTION_ADD, ['app\models\Event', 'sendInLog']);
$this->on(Event::ACTION_DELETE, ['app\models\Event', 'sendInLog']);
$this->on(Event::ACTION_UPDATE, ['app\models\Event', 'sendInLog']);
}

In initialize events in second parameter you may use current model or set other model. If you want use current model set like that:

[$this, 'sendInLog']

sendInLog it is method in model. In method sendInLog one parameter it is $event. This is object \yii\base\Event. In property $event->name - it is event name. In property $event->sender - it is model class from trigger event.

In my class app\models\Event like that:

namespace app\models;

class Event extends Component
{
const ACTION_ADD = 1;
const ACTION_DELETE = 2;
const ACTION_UPDATE = 3;

const TYPE_PROJECT = 10;
const TYPE_BIDS = 20;
const TYPE_BIDS_DATA = 30;

/**
* @param $event
*/
public static function sendInLog($event)
{
/** @var \yii\base\Event $event */
/** @var ActiveRecord $event->sender */
$userId = Yii::$app->user->id;
$model = new Logs([
'type' => $event->sender->getType(),
'action' => $event->name,
'id_user' => $userId,
'old_data' => Json::encode($event->sender->attributes),
'new_data' => Json::encode($event->sender->oldAttributes),
]);
$model->save();
}
}

Run trigger like that:

public function afterDelete()
{
$this->trigger(Event::ACTION_DELETE);
parent::afterDelete();
}

Or

public function actionView()
{
$this->trigger(Event::ACTION_VIEW);
$this->render(...);
}

EDIT:

For example. If you want run trigger after delete, insert, update. You may use trigger in afterDelete, afterSave in model. If you want run trigger in controller run trigger like that:

public function actionCreate()
{
$model = new Bids();
$model->id_project = Yii::$app->request->get('projectId');
$fieldsDefaults = BidsFieldsDefaults::find()->orderBy(['order' => SORT_ASC])->all();

if ($model->load(Yii::$app->request->post()) && $model->save()) {
$model->trigger(Event::ACTION_ADD);
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
'fieldsDefaults' => $fieldsDefaults
]);
}
}

I show you two different ways to run trigger. Which one to use is up to you :)

Yii2: Can I attach events from outside the model class?

Is there any possibility to add events to a model from another component?

Yes! You can use class level event handlers. The line of code below shows how to do that.

Event::on(ActiveRecord::className(), ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
Yii::debug(get_class($event->sender) . ' is inserted');
});

You can use same code in your init method and bind it to your class method instead of that closure.

I would create a class implementing BootstrapInterface and add it to config. Then I would handle those class level events there!

Do yourself a favour and read about events in the Guide as well as the API Documentation

Yii2 : Creating custom Global event

What looks like you are trying to create an event for the afterLogin, I would attach it to the controller rather than the model, and I would use a conventional approach to do that so you keep your code separate and clean.

Add a constant in the controller where you have the login action

const EVENT_AFTER_LOGIN = 'afterLogin';

Create an event handler class and add a handler function that would send the email

<?php
namespace common\components\handlers;

class LoginHandler
{
/**
* Handles the after login event process to send emails
*
* @param FormEvent $event Event object form
*
* @return null
*/
public static function handleAfterLogin(\common\components\events\FormEvent $event)
{
///.. your code to send emails
}
}

Create a FormEvent class

<?php

namespace common\components\events;

use yii\base\Event;
use yii\base\Model;

class FormEvent extends Event
{
/**
* @var Model
*/
private $_form;

/**
* @return Model
*/
public function getForm()
{
return $this->_form;
}

/**
* @param Model $form
*/
public function setForm(Model $form)
{
$this->_form = $form;
}
}

Create the init function in your controller like below

public function init()
{
parent::init();

//bind after confirmation event
$this->on(
self::EVENT_AFTER_LOGIN,
[
new \common\components\handlers\LoginHandler(),
'handleAfterLogin',
]
);
}

Now add the following method in your controller

 /**
* Return the FormEvent
*
* @param FormEvent $form the form model object
*
* @return FormEvent
* @throws \yii\base\InvalidConfigException
*/
protected function getFormEvent(\common\components\events\FormEvent $form)
{
return \Yii::createObject(['class' => \common\components\events\FormEvent::class, 'form' => $form]);
}

And then in your login action

public function actionLogin()
{
$model = Yii::createObject(LoginForm::class);

//get the event
$event = $this->getFormEvent($model);

if ($model->load(Yii::$app->request->post()) && $model->login()) {
//trigger the after login handler
$this->trigger(self::EVENT_AFTER_LOGIN, $event);
}
else{
return $this->render('login',['model' => $model]);
}
}

Now if you add print_r($event) inside you handleAfterLogin method you will see the object and its properties loaded with the user added info you can get the email using $event->username or whatever field name you have and add your code to send email in the handleAfterLogin.

Yii2 Events: How to add multiple event handlers using behavior's events method?

You can override Behavior::attach() so you can have something like this in your UserBehavior and no need of your events()

    // UserBehavior.php
public function attach($owner)
{
parent::attach($owner);
$owner->on(self::EVENT_NEW_USER, [$this, 'anotherOne']);
$owner->on(self::EVENT_NEW_USER, [$this, 'sendMailHanlder']);
}

Firing Events With Yii2

You can use class-level events for this. You can attach this on your app bootstrap (and probably in config, before app is initialized):

Event::on(
MyClass::class,
MyClass::FAILED_TRANSACTION_EVENT,
[MyEventHandlerClass::class, 'handle']
);

Then whenever MyClass will call $this->trigger(self::FAILED_TRANSACTION_EVENT);, MyEventHandlerClass::handle() will be called to handle this event.

You can also use global events. In your config:

'on failedTransaction' => [MyEventHandlerClass::class, 'handle'],

And trigger event by Yii::$app->trigger('failedTransaction') - this will also call MyEventHandlerClass::handle() to handle this event.

Yii2: How to use ActionEvent

If you want to use custom event, you should pass it as second argument of trigger():

$event = new MyEvent();
$event->helloMessage = $someValue;
$this->trigger(self::EVENT_HELLO, $event);
$isValid = $event->customEventResult;

This is explained in guide. You may take a look how ActionEvent is used in Controller::afterAction():

public function afterAction($action, $result)
{
$event = new ActionEvent($action);
$event->result = $result;
$this->trigger(self::EVENT_AFTER_ACTION, $event);
return $event->result;
}

BTW: ActionEvent is designed to control controller action flow - you should not use it for different purposes. You may want to create separate Event class if you need to handle custom event.

Yii2 - how to pass parameters in event

You should read this : Attaching Event Handlers.

When attaching an event handler, you may provide additional data as the third parameter to yii\base\Component::on(). The data will be made available to the handler when the event is triggered and the handler is called.

e.g. :

public function defaultJournal($event)
{
CsJournal::insertDefaultJournal($this, $event->data);
}

And then :

$this->on(self::EVENT_NEW_PORTAL, [$this, 'defaultJournal'], $userID);

How to know if the triggered events succeed or fail in Yii2

Usually you're using event object to store state of event. Create custom event:

class MyEvent extends Event {

public $isCommited = false;
}

Use it on trigger and check the result:

$event = new MyEvent();
$this->trigger('myEvent', $event);
if ($event->isCommited) {
// do something
}

In event handler you need to set this property:

function ($event) {
// do something
$event->isCommited = true;
}

If you want to break event flow you may use $handled property instead of isCommited and custom event.



Related Topics



Leave a reply



Submit