Thursday, February 11, 2016

PHP Yii2 Framework - Working with Events

An event is a way for a class to provide notifications to clients of that class when some action is occurred in an object to which the client classes may have interest. It provides a useful way for objects to signal state changes to clients of that object. When the event occurs, the handlers functions subscribed to it by its clients are invoked.

You can attach custom code to an event so that when the event is triggered, the code in the handler function gets executed. For example, a mailer object may trigger a messageSent event when it successfully sends a message. If you want to keep track of the messages that are successfully sent, you could then simply attach the tracking code to the messageSent event.

Yii2 provides a base class called yii\base\Component to support events. If you want a custom class needs to trigger events, you have to extend from yii\base\Component base class, or from a its child class.

Referenced from: http://www.yiiframework.com/doc-2.0/guide-concept-events.html

In this post I will demonstrate the events mechanism by developing an example.

Lets create a scenario, we have two classes Customer and Order, and both these classes we need to trigger an event on registration. We add methods RegisterCustomer and RegisterOrder to these classes, and will trigger events EVENT_CUSTOMER_REGISTERED and EVENT_ORDER_REGISTERED respectively. For demonstration purpose I will put a message in a session variable on each event, and for this we need to create a global function that will act as the single handler to events.

Then we use a separate view for displaying message reading from the session variable to verify if our event is successfully triggered and the handler function has actually placed an event message in session variable.

I have download the basic project template for http://www.yiiframework.com/download/, and start adding our code to this project already have all things setup to start.

First create a Customer class by extending from yii\base\Component base class. Here is the code listing for Customer.php:

<?php

namespace app\components;

use yii\base\Component;
use yii\base\Event;

class Customer extends Component
{
    const EVENT_CUSTOMER_REGISTERED = 'CUSTOMER_REGISTERED';

    public function RegisterCustomer()
    {
        $this->trigger(self::EVENT_CUSTOMER_REGISTERED);
    }
}

You might noticed that the basic project tempalte folder you downloaded, do not have the components folder by-default, you can simply create this folder in the root directory and save Customer.php file in this folder.

Similary we can create Order.php as follows:

<?php

namespace app\components;

use yii\base\Component;
use yii\base\Event;

class Order extends Component
{
    const EVENT_ORDER_REGISTERED = 'ORDER_REGISTERED';

    public function RegisterOrder()
    {
        $this->trigger(self::EVENT_ORDER_REGISTERED);
    }
}

I created a TestController with Index view as the home page for this demo, providing two buttons to register customer and order.

Register Customer button will call the actionRegisterCustomer action in TestController. Inside this action method, we create a Customer object, attach our global event handler function on this $customer object by using on function for attaching events. And finally for just demonstrating purpose I manually call RegisterCustomer function on $customer object which will trigger the event EVENT_CUSTOMER_REGISTERED. Then we simply return the render function call with view-message to display a message stored in session variable by global event handler function.

public function actionRegisterCustomer()
    {
        //create customer component object
        $customer = new Customer;

        //first clear session, so our event will set message in this session variable
        setSession('eventMessage', null);
 
        //attach event handler, 'genericEventHandlerFunction' is the name of global function with accepting one argument $event
        $customer->on(Customer::EVENT_CUSTOMER_REGISTERED, 'genericEventHandlerFunction','Customer register event data');
        
        //this function will trigger event
        $customer->RegisterCustomer();  

        //this view will display message generated by event
        return $this->render('view-message');
    }

Similarly the Register Order button will call the actionRegisterOrder action in TestController, and follows the same procedure for function call, attach event handler, event trigger function call, and finally return the same view-message;

public function actionRegisterOrder()
    {
        //create order component object
        $order = new Order;

        //first clear session, so our event will set message in this session variable
        setSession('eventMessage', null);
 
        //attach event handler, 'genericEventHandlerFunction' is the name of global function with accepting one argument $event
        $order->on(Order::EVENT_ORDER_REGISTERED, 'genericEventHandlerFunction','Order register event data');
        
        //this function will trigger event
        $order->RegisterOrder();  

        //this view will display message generated by event
        return $this->render('view-message');
    }

Now add a generic handler function which will store string message in session. Place this function in config\Globals.php file (if this file not already exists, you can create it to place all global functions or variables in one place).

function genericEventHandlerFunction($event)
{
    $msg = 'Event handled with data: ' . $event->data; 
    Yii::$app->session->set('eventMessage', $msg);
}

Finally let come to the view part, in our view-message.php, we extract the message from session and display it in the html.

<div class="alert alert-success"  >  

 <?php echo getSession('eventMessage'); ?>

</div>

It will display the following message when you clicked Register Customer button:

And you will get order registration message on Register Order button click:

References:

http://www.yiiframework.com/doc-2.0/guide-concept-events.html

4 comments:

  1. Hello,

    Thanks for awesome start guide.

    I have one question here is the where is the code of "genericEventHandlerFunction" ? where we have to create or define it ?

    ReplyDelete
    Replies
    1. Thanks Kalpesh, Yes it was missed. I appreciate your time for reading this article and pointing the mistake to improve. I have fixed it and updated the article.

      Delete
    2. Thanks Muhammad Idrees, I have created global function using following link : https://yii2-cookbook.readthedocs.io/structure-global-functions/

      Delete