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.

yii2-customer-order-trigger-event

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.

yii2-event-demo-view-read-message-from-session

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.

yii2-events-demo-buttons

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:

yii2-events-demo-customer-register-event-message

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

yii2-events-demo-order-register-event-message

References:

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

February 6, 2016

PHP Yii2 Framework - How to use JavaScript

In order to use JavaScript with Yii2 framework, we have to register scripts with yii\web\view object. Yii2 provides two methods for it

  • registerJs() for inline scripts
  • registerJsFile() for external script files.

registerJs() for inline scripts

Here is an example to register an inline script.

$script = "function test() {console.log('a meesage logged in console.'); alert('Hello World');}";

$this->registerJs($script, View::POS_END, 'my-options');

Where:

  1. First argument is the script content itself
  2. Second argument determines the place in the page where the script should be inserted, e.g. Head, Begin, End, Load etc.
  3. And the last argument is used to uniquely identify the code block

Here is our test html to call this javascript test() function.

&input type="button" value="Click Me" onclick="test();"\>

registerJsFile() for external script files.

An external JavaScript file can be added with the method registerJsFile().

$this->registerJsFile('http://localhost/yiidemo/js/main.js');

Registering with Asset Bundles

Another way to use JavaScript files in Yii2 framework is by registering it in asset bundles, and is preferred to use asset bundles instead of using JavaScript code/files directly You can use the following syntax to register current view for asset bundle.

\yiidemo\assets\AppAsset::register($this);

With the following javascipt configuration block in AppAsset.php

public $js = [
        '../assets/js/myjs1.js',
    ];

References:

http://www.yiiframework.com/doc-2.0/guide-output-client-scripts.html

January 25, 2016

How to Install Yii2 framework on Windows with XAMPP

In this post, I will explain how to install Yii2 framework on Windows, with XAMPP as PHP development environment.
First you have to install XAMPP you can find here: XAMPP Installer
You can install Yii2 framework by two ways:
  • Install Using Composer
  • Install Manually

Install Using Composer

The recommended approach is to install Yii2 framework using composer, because it will automatically install and configures all the required plugins and packages. You can download composer from https://getcomposer.org/download/. Composer setup will ask for PHP installation path, in my XAMPP installation it is:
D:\xampp\php\php.exe

You can verify if composer is correctly installed, by executing the command composer in command prompt. If you get an error message, it means it is not added in the Environment Variables, so you have to add the composer.exe folder path in Environment Variables.

composer

Next step is to install Composer assets plugin by executing the following command from command prompt:

composer global require "fxp/composer-asset-plugin:~1.1.1"

composer-asset-plugin

 
During installation of asset plugin, it will ask you for Github login credentials, because composer need to get API rate-limit to retrieve the dependent pacackges information from Github, you can find more information on this from Composer Documentation

Here we finished installing composer, and now we have to install Yii2. Move to a web-accessible folder, the folder where we usually place all web projects. In my XAMPP installation it is D:\xampp\htdocs
Now go to command prompt, move to the above mentioned web-accessible folder, and execute following command:
composer create-project --prefer-dist yiisoft/yii2-app-basic basic

composer-yii2-basic-project

 
It will install Yii2 framework to a folder named basic inside the htdocs folder, you can use your own project name in place of basic(last part in the above command). Then composer will download and configure all the required packages, and finally your Yii2 basic application is now ready. You can try access it by localhost path:
http://localhost/basic/web

Install Manually

Manuall Installation of Yii involves three steps:
  1. Download the archive file from yiiframework.com.
  2. Unpack the downloaded file to a Web-accessible folder, in our example it is D:\xampp\htdocs\basic
  3. Modify the config/web.php file by setting a secret key for the cookieValidationKey configuration item (this is done automatically if you are installing Yii using Composer):
        'cookieValidationKey' => 'enter your secret key here',
    

Thats it, you can access this new basic application by following url:
http://localhost/basic/web

You should see the following "Congratulations!" page in your browser.

yii-basic

If you did not see this page, please check if your PHP installation satisfies Yii's requirements. Just copy /requirements.php to /web/requirements.php and then access it via http://localhost/requirements.php. If you PHP development environment is properly configured, you should see the output similar to this:

yii-requirements


You should configure your PHP installation so that it meets the minimum requirements of Yii. Most importantly, you should have PHP 5.4 or above.

References

http://www.yiiframework.com/doc-2.0/guide-start-installation.html



January 21, 2016

AngularJS Filter - Limit filter on array of items for matching

AngularJS default filter enables us to filter items list according to a specified input text. By definition, It selects a subset of items from array and returns it as a new array.
For example we have the following list of users:
$scope.myList = [
{
  firstName: "Muhammad",
  lastName: "Idrees",
  description: "Idrees is working as Senior .Net Developer, connected with Ehsan and Faizan"
}, 
{
  firstName: "Ehsan",
  lastName: "Sajjad",
  description: "Ehsan is working as Senior .Net Developer, connected with Idrees"
},
{
  firstName: "Faizan",
  lastName: "Ahmed",
  description: "Faizan is technical consultant, connected with Idrees"
}
]
And here is our html markup, how we want to filter on this list:
 <body ng-app="demoApp" ng-controller="MyController" >
     <p >
        Search:
         <input type="text" ng-model="searchInput" / >
     </p >
     <p >
         <ul>
             <li ng-repeat="user in myList | filter: searchInput">
                 <h4 >{{user.firstName}} {{user.lastName}} </h4 >
                 <p >{{user.description}} </p >
             </li >
         </ul >
     </p >
 </body >
Now if we search for the name Ehsan by typing in the input field, it will display first two items, because both objects containing this search term (in firstname, lastname, or description).
But if I want to filter my list only for firstName, and do not want to match for lastName and description fields. This is how I can limit my filter to desired field.
 <ul>
   <li ng-repeat="user in myList | filter: { firstName : searchInput}" >
    <h4 >{{user.firstName}} {{user.lastName}} </h4 >
    <p >{{user.description}} </p >
   </li >
  </ul >
Now if we type any search term it will only look for that filter in the firstName field. If any record matches the term, it will be displayed.

January 14, 2016

WCF RIA Service - Get the hosted server url

While working on Microsoft LightSwitch Framework with Silverlight client, I encountered a problem, how to find the hosted URL/domain from within WCF RIA Service. Since I am working on local system with application hosted in IIS, and configured my application as virtual directory. So I want to know the address of application's virtual directory.
For example, consider this url:
http://localhost/MyWeb
Where localhost is my host name, and MyWeb is the application's virtual directory alias. There may be more nested pages inside, but we will ignore that, because we only want the url address of virtual directory.
Here is the solution:
We cannot access HttpContext class in silverlight/lightswitch client, so we have to call a proxy function hosted in WCF RIA Service, which will return the current hosted URL to client.
  • HttpContext.Current.Request.Url.Host will give you the host name.
  • HttpContext.Current.Request.ApplicationPath will give you the virtual directory name.
So concatenating these two url string parts will give us the path of virtual directory.
string virtualDirectoryHost = = HttpContext.Current.Request.Url.Host + HttpContext.Current.Request.ApplicationPath;
You might have to add namespace System.Web for HttpContext class or also might have to add reference to System.Web.dll.