I have model named Events with the following structure.
class Events extends \yii\db\ActiveRecord
{
const EVENT_SEND_EMAIL_TO_CREATER = 'send-email-to-creater-of-event';
public function init()
{
$this->on(self::EVENT_SEND_EMAIL_TO_CREATER, [$this, 'sendEmailToCreaterOfEvent']);
}
.....
public function getParents()
{
return $this->hasOne(Parents::className(), ['ID' => 'CreatedByUserID'])->select([ 'ID', 'Name' ]);
}
public function sendEmailToCreaterOfEvent($event)
{
echo '<pre>'; print_r($event->parents); exit;
}
}
The above event is triggered whenever a parent creates a new Event. But it giving the following error:
Getting unknown property: yii\base\Event::parents
Is relationship don't work within events? Please suggest.
$event refers to the instance of \yii\base\Event that was triggered, not to your active record Events. To access the object that triggered an event, you can use Event::sender:
echo '<pre>'; print_r($event->sender->parents); exit;
Alternatively since the event handler is in the same class as the object, you can use $this:
echo '<pre>'; print_r($this->parents); exit;
Related
I have a code for checking a session isset or not in Yii2, I added the code inside a function in a controller, this is the code
if(!isset($session['selectedMonth'])){
return $this->redirect(['select-period/month']);
return false;
}
I have over than 50 functions in 10 controllers, I want every function use that code, how do I can make it without put that code in every function one by one?
You could create base controller for your app, and extend all other controllers from it. Then you could add beforeAction() method in this base controller, so it will be used by all controllers that inherit from base controller:
public function beforeAction($action) {
// initialize $session here
if(!isset($session['selectedMonth'])){
Yii::$app->response->redirect(['select-period/month']);
return false;
}
return parent::beforeAction($action);
}
You can create simple behavior that will handle 'before action' event, for example:
use Yii;
use yii\base\Behavior;
use yii\base\Controller;
class RedirectBehavior extends Behavior
{
public function events()
{
return [
Controller::EVENT_BEFORE_ACTION => 'beforeAction',
];
}
public function beforeAction($event)
{
if (Yii::$app->session->has('selectedMonth')){
return;
}
Yii::$app->getResponse()->redirect(['select-period/month'])->send();
}
}
and attach it to your controllers
public function behaviors()
{
return [
'redirect' => [
'class' => RedirectBehavior::className(),
],
];
}
How do I let the event know that I'm needing to pass in 3 parameters to the handle method of my event.
config/web.php
'on eventname' => [EventName::class, 'handle'],
app/events/EventName.php
namespace app\events;
class EventName
{
public function handle($arg1, $arg2, $arg3)
{
}
}
Signature of your event handler is incorrect. Event handler takes only one argument - event object. If you need to pass three arguments to handler, you need to create custom event object and use its arguments to store these values.
Create custom event:
class MyEvent extends \yii\base\Event {
public $arg1;
public $arg2;
public $arg3;
}
Use it on event trigger:
$this->trigger('eventname', new MyEvent([
'arg1' => $arg1,
'arg2' => $arg2,
'arg3' => $arg3,
]));
And use event properties in handler:
public function handle(MyEvent $event) {
if ($event->arg1) {
// ...
}
}
I'm trying to change the $id param in my controller methods on the fly using beforeAction and a behavior. FYI, I'm going to use HashIds and need to convert anywhere I have a $_GET['id'] that may be hashed back into an integer.
How can I use a behavior to automatically modify my $_GET['id'] on the fly using a behavior?
An example action in my controller:
public function actionView($id){
// run code to process $id here back to integer using a behavior
echo $id; //should be an integer
}
My sample url: http://mydomain/posts/view?id=3QhLp
(Alternatively, perhaps the better way to do this is to create a custom url rule?)
you should implement a class that extends from the \yii\base\Behavior like below
<?php
namespace backend\models;
use Yii;
use yii\base\Behavior;
use yii\web\Controller;
class Transformer extends Behavior
{
public $id = '';
public function events()
{
return [Controller::EVENT_BEFORE_ACTION => 'transform']; //mounting the handler to the 'beforeAction' event on the controller.
}
public function transform()
{
$_GET['id'] = $this->id . "transformed"; //mock method here
return true;
}
}
Then in your controller, adding the code as follow:
public function behaviors()
{
return [
'transformer' => [
'class' => \backend\models\Transformer::className(), //Modify the path to your real behavior class.
'id' => Yii::$app->request->get('id'),
],
];
}
then access the Yii::$app->request->get('id') in your action, you will see the transformed url param.
Here is my Behavior's class events() method. When I trigger an event second handler i.e. sendMailHanlder gets called and it ignores anotherOne. I believe, the second one overwrites first one. How do I solve this problem so that both event handlers get called?
// UserBehavior.php
public function events()
{
return [
Users::EVENT_NEW_USER => [$this, 'anotherOne'],
Users::EVENT_NEW_USER => [$this, 'sendMailHanlder'],
];
}
// here are two handlers
public function sendMailHanlder($e)
{
echo ";
}
public function anotherOne($e)
{
echo 'another one';
}
One thing to notice is that I'm attaching this behavior to my Users.php model. I tried adding both handlers using model's init() method. That way both handlers got called. Here is my init code.
public function init()
{
$this->on(self::EVENT_NEW_USER, [$this, 'anotherOne']);
$this->on(self::EVENT_NEW_USER, [$this, 'sendMailHanlder']);
}
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']);
}
You can use anonymous function for attaching handler's in events method :
ActiveRecord::EVENT_AFTER_UPDATE => function ($event) {
$this->deleteRemovalRequestFiles();
$this->uploadFiles();
}
You should not use equal event names. Use this instead:
public function events()
{
return [
Users::EVENT_NEW_USER => [$this, 'sendMailHanlder'],
];
}
// Here are two handlers
public function sendMailHanlder($e)
{
echo '';
$this->anotherOne($e);
}
public function anotherOne($e)
{
echo 'another one';
}
I try to add a event after search onthe var_dump is excuted, but data did
not pass through. Why ?
trigger:
class ContentSearch extends Content
{
const EVENT_AFTER_SEARCH = 'afterSearch';
public function search($params)
{
$e = new ModelEvent;
$e->data = $this;
$this->trigger(self::EVENT_AFTER_SEARCH, $e);
}
}
on:
class ContentController extends Controller
{
public function actionIndex()
{
$searchModel = new ContentSearch();
$searchModel->on($searchModel::EVENT_AFTER_SEARCH, function ($event) {
var_dump($event->data);
die;
});
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
}
}
dump
null
Well, you are wrong about event data usage :
Read this : http://www.yiiframework.com/doc-2.0/yii-base-event.html#$data-detail
The data that is passed to yii\base\Component::on() when attaching an event handler.
And this : http://www.yiiframework.com/doc-2.0/yii-base-component.html#on()-detail
About the third param :
The data to be passed to the event handler when the event is triggered
Anyway, you don't need this, you could simply use $event->sender :
function ($event) {
var_dump($event->sender); // this will dump your ContentSearch model
die;
}