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
How to Set Cron Job Through PHP Script
Cross Domain Ajax Request with Jquery/Php
Tilde Operator in Regular Expressions
Use Curly Brackets to Structure Code in PHP
Call to Undefined Method Maatwebsite\Excel\Excel::Load()
PHP Try-Catch Blocks: Are They Able to Catch Invalid Arg Types
Transfer Variables Between PHP Pages
While ($Row = MySQL_Fetch_Array($Result)) - How Many Loops Are Being Performed
Disable Browser Cache in PHP or JavaScript in a Flash Application
PHP Run Once and Insert Twice in MySQL Database
Get the Hierarchy of a Directory with PHP
Php: Check for Duplicate Values in a Multidimensional Array
3Rd Party Dependency Conflict in Developing Wordpress Plugin