Events - uniqcle/Yii2 GitHub Wiki

  1. Назвать событие. Например, "Пользователь зарегестрировался"
  2. Создать действие/обработчик
  3. Навешать этот обработчик на событие
  4. Вызвать выполнение события

In Controller

навешиваем действия к событию frontend\controllers\UserController.php

namespace frontend\controllers;

use Yii; 
use frontend\models\forms\SignupForm; 
use frontend\models\forms\LoginForm; 
use frontend\models\User; 

class UserController extends \yii\web\Controller
{
    public function actionSignup(){	
    ...
    }

    public function actionLogin(){
    ...
    }

    public function actionLogout(){
    ...
    }

    public function actionEvents(){

         $user = new User(); 

         //СОЗДАЕМ И НАВЕШИВАЕМ ДЕЙСТВИЯ К СОБЫТИЮ 
         //В ней методы навызывания и отвязывания действий по отношению к событию on(), off()
         //on(Название события, само действие, кот. нужно сделать после этого события)
         $user->on(USER::USER_REGISTERED, function($event){
            var_dump($event->name);    //Выводит названия события
            var_dump($event->sender); //Та модель, в кот. было взывано событие
            var_dump($event->data);    // Доп. инфа, передается 3-м параметром после действия. Строка, массив, др. объект
            var_dump('Вызов события 1'); 
         }, 'Дополнительная информация'); 

         //Действия можно передать 4-мя способами
         #1 Callback. Как выше

         #2 Передать объект, 2-й параметр - метод в этом объекте
         $user->on(USER::USER_REGISTERED, [$user, 'methodFromObject']); 

         #3 Если в другой модели. 
         //Путь к этому классу и название статичного метода
         $user->on(USER::USER_REGISTERED, ['frontend\models\User', 'staticMethodFromClass']); 

         #4 Использование глобальных ф-ий PHP
         $user->on(USER::USER_REGISTERED, 'get_class' ); 

         //ВЫЗОВ СОБЫТИЯ
         $user->trigger(USER::USER_REGISTERED); 

    }
}

В модели User frontend\models\User.php

namespace frontend\models;

use Yii;
use yii\web\IdentityInterface; 

class User extends \yii\db\ActiveRecord implements IdentityInterface
{

    //НАЗЫВАЕМ СОБЫТИЕ
    //Константа с названием события
    const USER_REGISTERED = 'user registered'; 

    public static function tableName(){
        return 'user';
    }


    public function rules(){
    ...
    }

   ...
    public function methodFromObject($event){
        var_dump('Вызов события 2'); 
        $event->handled = true;      //После этого дальнейшие действия в события прекращаются.
    }

    public static function staticMethodFromClass(){
        var_dump('Вызов события 3'); 
    }
...
}

In Model

В методе init() навешиваем действия к событию

frontend\controllers\UserController.php

namespace frontend\controllers;

use Yii; 
use frontend\models\forms\SignupForm; 
use frontend\models\forms\LoginForm; 
use frontend\models\User; 

class UserController extends \yii\web\Controller
{
	
    public function actionSignup(){ }
    public function actionLogin(){ }

    public function actionLogout(){
    }

    public function actionEvents(){
         $user = new User(); 
         $user->userRegistered(); 
      }
}

Модель frontend\models\User.php

namespace frontend\models;

use Yii;
use yii\web\IdentityInterface; 

class User extends \yii\db\ActiveRecord implements IdentityInterface
{

    //НАЗЫВАЕМ СОБЫТИЕ
    //Константа с названием события
    const USER_REGISTERED = 'user registered'; 

    public function init(){
        //СОЗДАЕМ И НАВЕШИВАЕМ ДЕЙСТВИЯ К СОБЫТИЮ 
         //В ней методы навызывания и отвязывания действий по отношению к событию on(), off()
         //on(Название события, само действие, кот. нужно сделать после этого события)
         $this->on(USER::USER_REGISTERED, function($event){
            var_dump($event->name);    //Выводит названия события
            var_dump($event->sender); //Та модель, в кот. было взывано событие
            var_dump($event->data);    // Доп. инфа, передается 3-м параметром после действия. Строка, массив, др. объект
            var_dump('Вызов события 1'); 
         }, 'Дополнительная информация'); 

         //Действия можно передать 4-мя способами
         #1 Callback. Как выше

         #2 Передать объект, 2-й параметр - метод в этом объекте
         $this->on(USER::USER_REGISTERED, [$this, 'methodFromObject']); 

         #3 Если в другой модели. 
         //Путь к этому классу и название статичного метода
         $this->on(USER::USER_REGISTERED, ['frontend\models\User', 'staticMethodFromClass']); 

         #4 Использование глобальных ф-ий PHP
         $this->on(USER::USER_REGISTERED, 'get_class' ); 
    }

    
    public function userRegistered(){
         //ВЫЗОВ СОБЫТИЯ
         $this->trigger(USER::USER_REGISTERED); 
    }

    public function methodFromObject($event){
        var_dump('Вызов события 2'); 
        $event->handled = true;      //После этого дальнейшие действия в события прекращаются.
    }

    public static function staticMethodFromClass(){
        var_dump('Вызов события 3'); 
    }


    public static function tableName(){
        return 'user';
    }
    ...

Service as Component

Контроллер frontend\controllers\UserController.php

namespace frontend\controllers;

use Yii; 
use frontend\models\forms\SignupForm; 
use frontend\models\forms\LoginForm; 
use frontend\models\User; 

class UserController extends \yii\web\Controller
{
    //Register
    public function actionSignup()
    {	
    	$model = new SignupForm(); 
    	if($model->load( Yii::$app->request->post() ) && $user = $model->save() ){
            Yii::$app->user->login( $user ); 
    		Yii::$app->session->setFlash('success', 'New user added!'); 
    		return $this->redirect(['site/index']); 
    	}
        return $this->render('signup', [
        	'model' => $model
        ]);
    }

    //Login
    public function actionLogin(){
        $model = new LoginForm(); 
        if( $model->load( Yii::$app->request->post() ) && $model->login() ){
            Yii::$app->session->setFlash('success', 'Вы успешно авторизовались!');
            return $this->redirect(['/site/index']);  
        }

        return $this->render('login', [
            'model' => $model
        ]); 
    }

    public function actionLogout(){
        Yii::$app->user->logout(); 
        return $this->redirect(['site/index']); 
    }

}

В модели frontend\models\forms\SignupForm после валидации и сохранения данных пользователя в БД вместо вызова методов компонента, вызываем событие $user->userRegistered($event), в кот. подключены методы, а в нем передаем объект $event, куда записываем все данные по данному событию.

namespace frontend\models\forms; 

use Yii; 
use yii\base\Model; 
use frontend\models\User; 
use frontend\models\events\UserRegisterEvent; 

class SignupForm extends Model 
{
	public $username; 
	public $email; 
	public $password; 

	public function rules(){
		return [
			['username', 'string', 'min' => 2, 'max' => 255], 
			['username', 'required'], 
			['username', 'trim'], 
			['username', 'unique', 'targetClass' => User::className() ],

			['email', 'trim'], 
			['email', 'required'], 
			['email', 'email'],
			['email', 'string', 'min' => 2, 'max' => 255], 
			['email', 'unique', 'targetClass' => User::className() ],

			['password', 'required'], 
			['password', 'string', 'min' => 2, 'max' => 255]

		]; 
	}

	public function save(){
		
		$user = new User(); 

		if( $this->validate() ){

			$user->username             = $this->username; 
			$user->auth_key             = Yii::$app->security->generateRandomString(); 
			$user->password_hash        = Yii::$app->security->generatePasswordHash( $this->password ); 
			$user->email                = $this->email; 
			$user->created_at           = $time = time(); 
			$user->updated_at           = $time; 

			if( $user->save() ){

				//Объекта паттерна Data Transfer Object
				$event = new UserRegisterEvent(); 

				$event->user = $user; 
			        $event->subject = 'New user registered'; 

				//Вызов события
				$user->userRegistered($event); 

				/*
				Yii::$app->emailService->notifyUser($user, 'Welcome'); 
				Yii::$app->emailService->notifyAdmins('User registered'); */
				/*Yii::$app->smsService->notifyUser($user); 
				Yii::$app->smsService->notifyAdmins('User registered'); 
				Yii::$app->postService->sendGift($user); 
				other actions
				other actions
				other actions
				*/
		
				return $user; 
			} 

		}

		return false; 
	}
}

В модели frontend\models\User.php в методе init() подключаем обработчики к событию.

namespace frontend\models;

use Yii;
use yii\web\IdentityInterface; 
use common\components\UserNotificationInterface; 

class User extends \yii\db\ActiveRecord implements IdentityInterface
{

    //НАЗЫВАЕМ СОБЫТИЕ. Константа с названием события
    const USER_REGISTERED = 'user_registered'; 


    public function init(){
        //Навешиваем обработчик на событие. 1 парам - объект, 2 парам. - метод
        $this->on(self::USER_REGISTERED, [Yii::$app->emailService, 'notifyAdmins']);
        $this->on(self::USER_REGISTERED, [Yii::$app->emailService, 'notifyUser']);

        parent::init(); 
    }

    
    public function userRegistered($event){
          //ВЫЗОВ СОБЫТИЯ
         $this->trigger(USER::USER_REGISTERED, $event); 
    }



    public static function tableName()
    {
        return 'user';
    }


    public function rules()
    {
        return [
            [['username', 'auth_key', 'password_hash', 'email', 'created_at', 'updated_at'], 'required'],
            [['status', 'created_at', 'updated_at'], 'integer'],
            [['username', 'password_hash', 'password_reset_token', 'email'], 'string', 'max' => 255],
            [['auth_key'], 'string', 'max' => 32],
            [['username'], 'unique'],
            [['email'], 'unique'],
            [['password_reset_token'], 'unique'],
        ];
    }


    public function attributeLabels()
    {
        return [
            'id' => 'ID',
            'username' => 'Username',
            'auth_key' => 'Auth Key',
            'password_hash' => 'Password Hash',
            'password_reset_token' => 'Password Reset Token',
            'email' => 'Email',
            'status' => 'Status',
            'created_at' => 'Created At',
            'updated_at' => 'Updated At',
        ];
    }

    public function getUserByUsername($username){
        return self::find()->where(['username' => $username ])->one(); 
    }

    public function validatePassword($password){
        return Yii::$app->security->validatePassword($password, $this->password_hash); 
    }

    //IDENTITY INTERFACE
    public static function findIdentity($id)
    {
        return static::findOne($id);
    }

    public static function findIdentityByAccessToken($token, $type = null)
    {
        return static::findOne(['access_token' => $token]);
    }

    public function getId()
    {
        return $this->id;
    }


    public function getAuthKey()
    {
        return $this->auth_key;
    }


    public function validateAuthKey($authKey)
    {
        return $this->getAuthKey() === $authKey;
    }
}

Класс компонента common\components\EmailService.php

namespace common\components; 

use Yii; 
//Чтобы использовать этот класс как компонент приложения, нужно расширить от Component
use yii\base\Component; 
use common\components\UserNotificationInterface; 

class EmailService extends Component 
{	
	public function notifyUser(UserNotificationInterface $event){

		Yii::$app->mailer->compose() 
			->setFrom( '[email protected]' ) 
			->setTo( $event->getEmail() )  
			->setSubject( $event->getSubject() )  
			->setTextBody('Новый клиент') //  Закомментировать, если передаем в View
			/*->setHtmlBody('Текст...')*/
			->send();
	}


	public function notifyAdmins(UserNotificationInterface $event){
		Yii::$app->mailer->compose() 
			->setFrom( '[email protected]' ) 
			->setTo( '[email protected]' )  
			->setSubject( $event->getSubject() )  
			->setTextBody('Новый клиент') //  Закомментировать, если передаем в View
			/*->setHtmlBody('Текст...')*/
			->send();
	}
}

Класс frontend\models\events\UserRegisterEvent.php, в кот. будет передаваться данные для события

namespace frontend\models\events; 

use yii\base\Event; 
use frontend\models\User; 
use common\components\UserNotificationInterface; 

class UserRegisterEvent extends Event implements UserNotificationInterface
{
	public $user; 
	public $subject; 

	public function getUser(){
		return $this->user; 
	}

	public function getEmail(){
		return $this->user->email; 
	}

	public function getSubject(){
		return $this->subject; 
	}
}

Интерфейс common\components\UserNotificationInterface.php необходим для контроля типов и какие методы должен реализовывать переходный класс UserRegisterEvent

namespace common\components; 

interface UserNotificationInterface 
{
	public function getEmail(); 
	public function getSubject(); 
}

Проверить настройки в подключении компонентов common\config\main.php

...
    'components' => [
        'cache' => [
            'class' => 'yii\caching\FileCache',
        ],
        /// Компонент почты
        'mailer' => [
                    'class' => 'yii\swiftmailer\Mailer',
                    'viewPath' => '@common/mail',
                    'transport' => [
                        'class' => 'Swift_SmtpTransport',
                        'host' => 'smtp.yandex.ru',
                        'username' => '[email protected]', //
                        'password' => '2207kirill',
                        'port' => '465',
                        'encryption' => 'ssl', // у яндекса SSL
                    ],
         
                    'useFileTransport' => false, // будем отправлять реальные сообщения, а не в файл
                ],
        // Подключаем компонент сервиса
        'emailService' => [
            'class' => 'common\components\EmailService'
        ],
    ],