Book Image

Yii Project Blueprints

By : Charles R. Portwood ll
Book Image

Yii Project Blueprints

By: Charles R. Portwood ll

Overview of this book

Table of Contents (15 chapters)
Yii Project Blueprints
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Creating the presentation layer


Up until this point, all the code that has been written is backend code that the end user won't be able to see. In this section, we will be creating the presentation layer of our application. The presentation layer of our application is composed of three components: controllers, layouts, and views. For this next section, we'll be creating all the three components.

As a developer, we have several options to create the presentation layer. One way we can create the presentation layer is using Gii. Gii has several built-in tools that can assist you in creating new controllers, forms for our views, and even full create, read, update, and delete (CRUD) skeletons for our application. Alternatively, we can write everything by hand.

Managing projects

The first part of the presentation layer we are going to work on is the projects section. To begin with, create a new file in protected/controllers/ called ProjectControllerProjectController.php that has the following class signature:

<?php
class ProjectControllerProjectController extends CController {}

For our controllers, we will be extending Yii's base class called CController. In future chapters, we will create our own controllers and extend the controllers from them.

Before we can start displaying content from our new action, we'll need to create a layout for our content to be rendered in. To specify our layout, create a public property called $layout, and set the value to 'main':

public $layout = 'main';

Next, let's create our first action to make sure everything is working:

public function actionIndex()
{
    echo "Hello!";
}

Now, we should be able to visit http://localhost/projects/index from our web browser and see the text Hello printed on the screen. Before we continue defining our actions, let's create a layout that will help our application look a little better.

Creating the layout

The layout that we specified references the file located in protected/views/layouts/main.php. Create this file and open it for editing. Then, add the following basic HTML5 markup:

<!DOCTYPE html>
<html>
   <head>
   	</head>
   <body>
   </body>
</html>

Then add a title within the <head> tag that will display the application name we defined in protected/config/main.php:

<title><?php echo Yii::app()->name; ?></title>

Next, let's add a few meta tags, CSS, and scripts. To reduce the number of files we need to download, we'll be including styles and scripts from a publicly available Content Distribution Network (CDN). Rather than writing markup for these elements, we're going to use CClientScript, a class made to manage JavaScript, CSS, and meta tags for views.

For this application, we'll be using a frontend framework called Twitter Bootstrap. This framework will style many of the common HTML tags that our application will use, providing it with a cleaner overall look.

Tip

When you're ready to go live with your application, you should consider moving the static assets you are using to a CDN, referencing popular libraries such as Twitter Bootstrap and jQuery from a publicly available CDN. CDNs can help to reduce hosting costs by reducing the amount of bandwidth your server needs to use to send files. Using a CDN can also speed up your site since they usually have servers geographically closer to your users than your main server.

First, we're going to call CClientScript, as follows:

<?php $cs = Yii::app()->clientScript; ?>

Secondly, we're going to set the Content-Type to text/html with a UTF-8 character set, as follows:

<?php $cs->registerMetaTag('text/html; charset=UTF-8', 'Content-Type'); ?>

Next, we're going to register the CSS from Twitter Bootstrap 3 from a popular CDN, as follows:

<?php $cs->registerCssFile( '//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css' ); ?>

Then we'll register the JavaScript library for Twitter Bootstrap:

<?php $cs->registerScriptFile( '//netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js' ); ?>

Finally, we're going to register jQuery 2.0 and have Yii placed at the end of the <body> tag, as follows:

<?php $cs->registerScriptFile( '//code.jquery.com/jquery.js', CClientScript::POS_END ); ?>

CClientScript also supports method chaining, so you could also change the preceding code to the following:

<?php Yii::app()->clientScript
        	->registerMetaTag('text/html; charset=UTF-8', 'Content-Type')
        	->registerCssFile( '//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css'
        	->registerScriptFile( '//netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js' )
        	->registerScriptFile( 'https://code.jquery.com/jquery.js' , CClientScript::POS_END); ?>

For the last part of our layout, let's add a basic header within our <body> tag that will help with navigation, as follows:

<div class="row">
    <div class="container">
        <nav class="navbar navbar-default navbar-fixed-top" role="navigation">
            <div class="navbar-header">
                <a class="navbar-brand" href="/"><?php echo CHtml::encode(Yii::app()->name); ?></a>
             </div>
        </nav>
    </div>
   </div>

After the closing </div> tag, add the following:

<div class="row" style="margin-top: 100px;">
    <div class="container">
        <?php echo $content; ?>
    </div>
</div>

The $content variable that we've added to our layout is a special variable that contains all the rendered HTML markup from our view files and is defined by the CController class in the render() method. Yii will automatically populate this variable for us whenever we call the render() method from within our controllers.

Creating the project index action

With our layout defined, we can get back to creating actions. Let's start by modifying our actionIndex() method so that it renders a view.

First, create a variable to store a searchable copy of our model. Have a look at the following code:

$model = new Projects('search');

Next, render a view called index, which references protected/views/projects/index.php, and pass the model we created to this view, as follows:

$this->render('index', array('model' => $model));

Now, create the view file in protected/views/projects/index.php and open it for editing. Begin by adding a button in the view as follows, which will reference the save action that we will create later on:

<?php echo CHtml::link('Create New Project', $this->createUrl('/projects/save'), array('class' => 'btn btn-primary pull-right')); ?>
<div class="clearfix"></div>

Then add a descriptive title so that we know what page we are on. Have a look at the following line of code:

<h1>Projects</h1>

Finally, create a new widget that uses CListView, a built-in Yii widget designed for displaying data from CActiveDataProvider. In Yii, widgets are frontend components that help us to quickly generate commonly used code, typically for presentation purposes. This widget will automatically generate pagination for us as necessary and will allow each of our items to look the same. Have a look at the following code:

<?php $this->widget('zii.widgets.CListView', array(
    'dataProvider'=>$model->search(),
    'itemView'=>'_project',
)); ?>

The new widget that we created consists of two parts. The first is the dataProvider, which provides data to the widget. This data comes from our project's model's search() method, a piece of code automatically generated by Gii.

The second part of the widget is the itemView, which references the specific view file that our items will be rendered out of. In this case, the view references a file in the same directory of protected/views/projects called _project.php. Create this file and then add the following code to it:

<div>
    <div class="pull-left">
        <p><strong><?php echo CHtml::link(CHtml::encode($data->name), $this->createUrl('/projects/tasks', array('id' => $data->id))); ?></strong></p>
        <p>Due on <?php echo date('m/d/Y', $data->due_date); ?></p>
<?php if ($data->completed): ?>
            Completed
        <?php else: ?>
            <?php if ($data->numberOfTasks == 0): ?>
                <p>No Tasks</p>
            <?php else: ?>
                <p><?php echo $data->getPercentComplete(); ?>% Completed</p>
            <?php endif; ?>
        <?php endif; ?>
    </div>
    <div class="pull-right">
        <?php echo CHtml::link(NULL, $this->createUrl('/projects/save', array('id' => $data->id)), array('title' => 'edit', 'class' => 'glyphicon glyphicon-pencil')); ?>
        <?php echo CHtml::link(NULL, $this->createUrl('/projects/complete', array('id' => $data->id)), array('title' => $data->completed == 1 ? 'uncomplete' : 'complete', 'class' => 'glyphicon glyphicon-check')); ?>
        <?php echo CHtml::link(NULL, $this->createUrl('/projects/delete', array('id' => $data->id)), array('title' => 'delete', 'class' => 'glyphicon glyphicon-remove')); ?>
    </div>
    <div class="clearfix"></div>
</div>
<hr/>

If we refresh our browser page now, our view will show us that no results were found. Before we can see data, we need to create an action and view to create and update it. Before we start creating new records, let's create two other actions that we outlined in our item's view: complete and delete.

Changing a project's completion state

First, let's create an action to mark a project as completed or uncompleted. This action will only be responsible for changing the completed field of the projects table to 0 or 1, depending on its current state. For simplicity, we can just XOR the field by 1 and save the model. Have a look at the following code:

public function actionComplete($id)
{
    $model = $this->loadModel($id);
    $model->completed ^= 1;
    $model->save();
    $this->redirect($this->createUrl('/projects'));
}

Additionally, we'll create another private method called loadModel(), which will load our appropriate model for us and throw an error if it cannot be found. For this method, we'll use CHttpException, which will create an HTTP exception with the error message we provide if a model with the specified ID cannot be found. Have a look at the following code:

private function loadModel($id)
{
    $model = Projects::model()->findByPk($id);
    if ($model == NULL)
        throw new CHttpException('404', 'No model with that ID could be found.');
    return $model;
}

Deleting projects

Next, we'll create a method to delete the project. This method will use the loadModel() method we defined earlier. Additionally, if we encounter an error deleting the model, we'll throw an HTTP exception so that the user knows something went wrong. Here's how we go about it:

public function actionDelete($id)
{
    $model = $this->loadModel($id);

    if ($model->delete())
        $this->redirect($this->createUrl('/projects'));

    throw new CHttpException('500', 'There was an error deleting the model.');
}

Creating and updating projects

With the two other methods defined, we can now work on creating and updating a project. Rather than creating two actions to handle both these tasks, we're going to create one action that knows how to handle both by checking the ID that we'll pass as a GET parameter. We can do that by defining a new action that looks as follows:

public function actionSave($id=NULL) {

We can then either create a new project or update a project based upon whether or not we were provided with an ID by the user. By taking advantage of loadModel(), we also take care of any errors that would occur if an ID was provided but a project with that ID didn't exist. Have a look at the following code:

if ($id == NULL)
    $model = new Projects;
else
    $model = $this->loadModel($id);

Next, we can detect whether the user submitted data by checking the $_POST variable for an array called Projects. If that array is defined, we'll assign it to our $model->attributes object. Before saving the model, however, we'll want to convert whatever the user entered into a Unix timestamp. Have a look at the following code:

if (isset($_POST['Projects']))
{
    $model->attributes = $_POST['Projects'];
    $model->due_date = strtotime($_POST['Projects']['due_date']);
    $model->save();
}

Finally, we'll render the view and pass the model down to it, as follows:

$this->render('save', array('model' => $model));

Create a new file in protected/views/projects/ called save.php and open it to edit. Begin by adding a header that will let us know whether we are editing a project or creating a new one, as follows:

<h1><?php echo $model->isNewRecord ? 'Create New' : 'Update'; ?> Project</h1>

Next, we'll create a new widget with CActiveForm, which will take care of the hard tasks of creating and inserting form fields into our view file (such as what the names and IDs of form fields should be):

<?php $form=$this->beginWidget('CActiveForm', array(
    'id'=>'project-form',
    'htmlOptions' => array(
        'class' => 'form-horizontal',
        'role' => 'form'
    )
)); ?>
<?php $this->endWidget(); ?>

Between the beginWidget and endWidget call, add an error summary if the user encounters an error:

<?php echo $form->errorSummary($model); ?>

Then, after the error summary, add the form fields and their associated styles, as follows:

<div class="form-group">
    <?php echo $form->labelEx($model,'name', array('class' => 'col-sm-2 control-label')); ?>
    <div class="col-sm-10">
        <?php echo $form->textField($model,'name', array('class' => 'form-control')); ?>
    </div>
</div>

<div class="form-group">
    <?php echo $form->labelEx($model,'completed', array('class' => 'col-sm-2 control-label')); ?>
    <div class="col-sm-10">
        <?php echo $form->dropDownList($model,'completed', array('0' => 'No','1' => 'Yes'), array('class' => 'form-control')); ?>
    </div>
</div>

<div class="form-group">
    <?php echo $form->labelEx($model,'due_date', array('class' => 'col-sm-2 control-label')); ?>
    <div class="col-sm-10">
        <div class="input-append date">
MM/DD/YYYY
            <?php $this->widget('zii.widgets.jui.CJuiDatePicker', array(
                    'model' => $model,
                    'attribute' => 'due_date',
                    'htmlOptions' => array(
                        'size' => '10',
                        'maxlength' => '10',
                        'class' => 'form-control',
                        'value' => $model->due_date == "" ? "" : date("m/d/Y", $model->due_date)
                    ),
                )); ?>		
</div>
    </div>
</div>

<div class="row buttons">
    <?php echo CHtml::submitButton($model->isNewRecord ? 'Create' : 'Save', array('class' => 'btn btn-primary pull-right')); ?>
</div>

Note

Did you notice how we're taking advantage of the Yii widget called CJuiDatePicker? This widget will provide us with a clean interface for selecting dates from a calendar view, rather than requiring our end user to type in the date manually and in the specified format we've requested.

Now we can create, update, view, and delete projects. Additionally, we've created an easy action to mark them as completed. Before we're done with this controller, we need to add an action that allows us to view tasks in our project.

Viewing tasks

Our tasks action for this controller will function in the same manner as our index action but will instead use a view called tasks:

public function actionTasks($id=NULL)
{
    if ($id == NULL)
        throw new CHttpException(400, 'Missing ID');

    $project = $this->loadModel($id);
    if ($project === NULL)
        throw new CHttpException(400, 'No project with that ID exists');

    $model = new Tasks('search');
    $model->attributes = array('project_id' => $id);

    $this->render('tasks', array('model' => $model, 'project' => $project));
}

The tasks.php view in protected/views/projects/tasks.php will look as follows:

<?php echo CHtml::link('Create New Task', $this->createUrl('/tasks/save?Tasks[project_id]=' . $project->id), array('class' => 'btn btn-primary pull-right')); ?>
<div class="clearfix"></div>
<h1>View Tasks for Project: <?php echo $project->name; ?></h1>
<?php $this->widget('zii.widgets.CListView', array(
    'dataProvider'=>$model->search(),
    'itemView'=>'_tasks',
));
?>

The _tasks.php item view in protected/views/projects/tasks.php will look as follows:

<div>
    <div class="pull-left">
        <p><strong><?php echo CHtml::link(CHtml::encode($data->title), $this->createUrl('/tasks/save', array('id' => $data->id))); ?></strong></p>
        <p>Due on <?php echo date('m/d/Y', $data->due_date); ?></p>
    </div>
    <div class="pull-right">
        <?php echo CHtml::link(NULL, $this->createUrl('/tasks/save', array('id' => $data->id)), array('class' => 'glyphicon glyphicon-pencil')); ?>
        <?php echo CHtml::link(NULL, $this->createUrl('/tasks/complete', array('id' => $data->id)), array('title' => $data->completed == 1 ? 'uncomplete' : 'complete', 'class' => 'glyphicon glyphicon-check')); ?>
        <?php echo CHtml::link(NULL, $this->createUrl('/tasks/delete', array('id' => $data->id)), array('class' => 'glyphicon glyphicon-remove')); ?>
    </div>
    <div class="clearfix"></div>
</div>
<hr/>

Managing tasks

Now that we can manage projects, let's work on managing tasks. Our TasksController is going to be nearly identical to our project's controller with only a few differences. Start by creating a new file in protected/controllers called TasksController.php that has the following signature:

<?php class TasksController extends CController {}

By only making a small change to our loadModel() method, we can reuse the delete and complete action from our projects controller, as follows:

private function loadModel($id)
{
    $model = Tasks::model()->findByPk($id);
    if ($model == NULL)
        throw new CHttpException('404', 'No model with that ID could be found.');
    return $model;
}

Our save action is almost identical to our project's save action. Have a look at the following code:

public function actionSave($id=NULL)
{
    if ($id == NULL)
        $model = new Tasks;
    else
        $model = $this->loadModel($id);

    if (isset($_GET['Tasks']))
        $model->attributes = $_GET['Tasks'];

    if (isset($_POST['Tasks']))
    {
        $model->attributes = $_POST['Tasks'];
        $model->due_date = strtotime($_POST['Tasks']['due_date']);
        $model->save();
    }

    $this->render('save', array('model' => $model));
}

The view file for this action is almost the same as well. If you haven't already, create a file called save.php in protected/views/tasks/, and then add the following lines of code to finish the view:

<ol class="breadcrumb">
  <li><?php echo CHtml::link('Project', $this->createUrl('/projects')); ?></li>
  <li class="active"><?php echo $model->isNewRecord ? 'Create New' : 'Update'; ?> Task</li>
</ol>
<hr />
<h1><?php echo $model->isNewRecord ? 'Create New' : 'Update'; ?> Task</h1>
<?php $form=$this->beginWidget('CActiveForm', array(
    'id'=>'project-form',
    'htmlOptions' => array(
        'class' => 'form-horizontal',
        'role' => 'form'
    )
)); ?>
    <?php echo $form->errorSummary($model); ?>

    <div class="form-group">
        <?php echo $form->labelEx($model,'title', array('class' => 'col-sm-2 control-label')); ?>
        <div class="col-sm-10">
            <?php echo $form->textField($model,'title', array('class' => 'form-control')); ?>
        </div>
    </div>

    <div class="form-group">
        <?php echo $form->labelEx($model,'data', array('class' => 'col-sm-2 control-label')); ?>
        <div class="col-sm-10">
            <?php echo $form->textArea($model,'data', array('class' => 'form-control')); ?>
        </div>
    </div>

    <div class="form-group">
        <?php echo $form->labelEx($model,'project_id', array('class' => 'col-sm-2 control-label')); ?>
        <div class="col-sm-10">
            <?php echo $form->dropDownList($model,'project_id', CHtml::listData(Projects::model()->findAll(), 'id', 'name'), array('empty'=>'Select Project', 'class' => 'form-control')); ?>
        </div>
    </div>

    <div class="form-group">
        <?php echo $form->labelEx($model,'completed', array('class' => 'col-sm-2 control-label')); ?>
        <div class="col-sm-10">
            <?php echo $form->dropDownList($model,'completed', array('0' => 'No','1' => 'Yes'), array('class' => 'form-control')); ?>
        </div>
    </div>

    <div class="form-group">
        <?php echo $form->labelEx($model,'due_date', array('class' => 'col-sm-2 control-label')); ?>
        <div class="col-sm-10">
            <div class="input-append date">
                <?php $this->widget('zii.widgets.jui.CJuiDatePicker', array(
                    'model' => $model,
                    'attribute' => 'due_date',
                    'htmlOptions' => array(
                       'size' => '10',
                       'maxlength' => '10',
                        'class' => 'form-control',
                       'value' => $model->due_date == "" ? "" : date("m/d/Y", $model->due_date)
                    ),
                )); ?>			</div>
        </div>
    </div>
    
    <div class="row buttons">
        <?php echo CHtml::submitButton($model->isNewRecord ? 'Create' : 'Save', array('class' => 'btn btn-primary pull-right')); ?>
    </div>

<?php $this->endWidget(); ?>

Preventing unauthorized access to our application

Our tasks application can now do everything we defined in our requirements. However, it is open to the world. Anyone who wants to edit our tasks could simply visit our website and change anything without our knowledge. Before finishing up, let's create a simple authentication system to protect our data.

Requiring authentication with filters and access rules

The first part in protecting our application is making sure that only authorized people can visit our application. We can do this by adding a filter to our controller called accessControl and defining access rules to access our content.

A filter is a piece of code that gets executed before (and/or after) a controller action runs, which means that the user will be required to be authenticated before accessing our content. To add the accessControl filter, add the following to both TasksController and ProjectsController:

public function filters()
{
    return array(
        'accessControl',
    );
}

Next, create a new method called accessRules(), which will define what users can access our application. For our application, we want to deny access to anyone who isn't authenticated. Have a look at the following code snippet:

public function accessRules()
{
    return array(
        array('allow',
            'users'=>array('@'),
        ),
        array('deny',  // deny all users
            'users'=>array('*'),
         ),
    );
}

In the preceding array, @ is a shorthand reference to an authenticated user. Now if we try to visit our web page, we'll be redirected to /site/login, the default login action in Yii.

Creating a controller for the authentication

Create a file called SiteController.php in protected/controllers, and then create login and logout actions as follows:

<?php
class SiteController extends CController
{
    public $layout = 'signin';
    
    public function actionLogin()
    {
        $model = new LoginForm;

        if (isset($_POST['LoginForm']))
        {
            $model->attributes = $_POST['LoginForm'];
            if ($model->login())
                $this->redirect($this->createUrl('/projects'));
        }
        $this->render('login', array('model' => $model));
    }

    public function actionLogout()
    {
        Yii::app()->user->logout();
        $this->redirect($this->createUrl('/site/login'));
    }
}

Creating a login layout

For this controller, we're going to create a new layout called login.php in protected/views/layouts. Copy the markup from protected/views/layouts/main.php to our new layout, and replace the contents of the <body> tag with the following:

<div class="row">
    <div class="container">
        <?php echo $content; ?>
    </div>
</div>

To make our login page look more like a login page, add the following CSS to the layout either as an inline style or as a separate file in /css/signup.css:

body {
  padding-top: 40px;
  padding-bottom: 40px;
  background-color: #eee;
}

.form-signin {
  max-width: 330px;
  padding: 15px;
  margin: 0 auto;
}
.form-signin .form-signin-heading,
.form-signin .checkbox {
  margin-bottom: 10px;
}
.form-signin .checkbox {
  font-weight: normal;
}
.form-signin .form-control {
  position: relative;
  font-size: 16px;
  height: auto;
  padding: 10px;
  -webkit-box-sizing: border-box;
     -moz-box-sizing: border-box;
          box-sizing: border-box;
}
.form-signin .form-control:focus {
  z-index: 2;
}
.form-signin input[type="text"] {
  margin-bottom: -1px;
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}
.form-signin input[type="password"] {
  margin-bottom: 10px;
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}

Creating a login view

Create a new form in protected/views/site/login.php that will hold our login model, as follows:

<?php $form=$this->beginWidget('CActiveForm', array(
    'id'=>'login-form',
    'enableClientValidation'=>true,
    'htmlOptions' => array(
            'class' => 'form-signin',
            'role' => 'form'
    ),
    'clientOptions'=>array(
        'validateOnSubmit'=>true,
    ),
)); ?>

    <?php if (!Yii::app()->user->isGuest): ?>
        <h2 class="form-signin-heading">You are already signed in! Please <?php echo CHtml::link('logout', $this->createUrl('/site/logout')); ?> first.</h2>
    <?php else: ?>
        <h2 class="form-signin-heading">Please sign in</h2>
        <?php echo $form->errorSummary($model); ?>
        <?php echo $form->textField($model,'username', array('class' => 'form-control', 'placeholder' => 'Username')); ?>
        <?php echo $form->passwordField($model,'password', array('class' => 'form-control', 'placeholder' => 'Password')); ?>
        <?php echo CHtml::tag('button', array('class' => 'btn btn-lg btn-primary btn-block'), 'Submit'); ?>
    <?php endif; ?>
<?php $this->endWidget(); ?>

Identifying our users with the UserIdentity CUserIdentity class

Before we create our login model, we need to create a way to identify our users. Fortunately, Yii has a built-in class to handle this called CUserIdentity. By easily extending CUserIdentity, we can create a key-value login pair that will ensure that only authenticated users can log in to our application.

Create a new file called UserIdentity.php in /components, and add the following:

<?php
class UserIdentity extends CUserIdentity
{
    public function authenticate()
    {
        $users=array(
            'demo'=>'demo',
            'admin'=>'admin',
        );
        if(!isset($users[$this->username]))
            $this->errorCode=self::ERROR_USERNAME_INVALID;
        elseif($users[$this->username]!==$this->password)
            $this->errorCode=self::ERROR_PASSWORD_INVALID;
        else
            $this->errorCode=self::ERROR_NONE;
        return !$this->errorCode;
    }
}

The authenticate() method of UserIdentity is what we'll use in our login model to ensure that we have valid credentials. In this class, we are simply checking whether the username that will be sent to this class by our login model matches the key associated with it. If a user's password does not match the key in our $users array, or if the user is not defined in our $users array, we return an error code.

Creating the login model

The last component we need to authenticate our users is to create a generic model to authenticate the user against. Begin by creating a new file called LoginForm.php in protected/models. This class will extend CFormModel, a generic model in Yii for forms, as follows:

<?php class LoginForm extends CFormModel {

Since CFormModel doesn't connect to a database, we defined attributes as public properties, as follows:

public $username;
public $password;
private $_identity;

Our model also needs validation rules to verify that we have a valid user. In addition to making sure username and password are provided, we're going to provide an additional validation rule called authenticate, which will validate that we have a valid username and password. Have a look at the following lines of code:

public function rules()
{
    return array(
        array('username, password', 'required'),
        array('password', 'authenticate'),
    );
}

Because our authenticate() method is a custom validator, its method signature has two parameters, $attribute and $params, which have information about the attribute and parameters that may have been passed from the validator. This method will determine whether our credentials are valid. Have a look at the following code:

public function authenticate($attribute,$params)
{
    if(!$this->hasErrors())
    {
        $this->_identity=new UserIdentity($this->username,$this->password);
        if(!$this->_identity->authenticate())
            $this->addError('password','Incorrect username or password.');
    }
}

Finally, we'll create the login() method that our SiteController calls. In addition to validating our credentials, it will do the heavy lifting of creating a session for the user. Have a look at the following code:

public function login()
{
    if (!$this->validate())
        return false;

    if ($this->_identity===null)
    {
        $this->_identity=new UserIdentity($this->username,$this->password);
        $this->_identity->authenticate();
    }

    if ($this->_identity->errorCode===UserIdentity::ERROR_NONE)
    {
        $duration = 3600*24*30;
        Yii::app()->user->login($this->_identity,$duration);
        return true;
    }
    else
        return false;
}

Now you can visit our site and log in with the credentials provided in our UserIdentity.php file.