Build a Full Featured Login and Registration System With YII
Form validation, login and registration are a critical issue to learning any full stack framework like Yii. Yii gives us these features very well defined manner and Yii makes it easy for us. In this tutorial, I will show you how to build a full-featured login and registration system with Yii framework. Before start, I assume you already know about Yii GiiModule, Yii CRUD operation and form validation processes. If don’t, read those first.
Necessary class definitions –
CUserIdentity – It is a base class for cehcking identities that are authenticated based on a username and a password.
We should derive classes and implement authenticate with the actual authentication scheme (e.g. checking username and password against a DB table). Be sure to check out the top10slotgames.com as a sample website using the Yii feature on their system.
By default, CUserIdentity assumes the username is a unique identifier and thus use it as the ID of the identity. However, it is possible to change the behavior of the identity checking functionality of CUserIdentity class.
CCaptcha – In this tutorial we will use captcha to process of our registration so that, we can assure, only human are registering into our system.
CCaptcha class renders a CAPTCHA image element. CCaptcha is used together with CCaptchaAction class to provide CAPTCHA – a way of preventing site spam.
The image element rendered by CCaptcha will display a CAPTCHA image generated by an action of class CCaptchaAction belonging to the current controller. By default, the action ID should be ‘captcha’, which can be changed by setting captchaAction.
CCaptcha may also render a button next to the CAPTCHA image. Clicking on the button will change the CAPTCHA image to be a new one in an AJAX way.
If clickableImage is set true, clicking on the CAPTCHA image will refresh the CAPTCHA.
CCaptchaAction – CCaptchaAction renders a CAPTCHA image. CCaptchaAction is used together with CCaptcha and CCaptchaValidator to provide the CAPTCHA feature. You must configure properties of CCaptchaAction to customize the appearance of the generated image.
Note, CCaptchaAction requires PHP GD2 extension. So, make sure that your DG2 extension is enabled with your PHP installer.
Using CAPTCHA involves the following steps:
- Override CController::actions() and register an action of class CCaptchaAction with ID ‘captcha’.
- In the form model, declare an attribute to store user-entered verification code, and declare the attribute to be validated by the ‘captcha’ validator.
- In the controller view, insert a CCaptcha widget in the form.
CViewAction – CViewAction represents an action that displays a view according to a user-specified parameter.
By default, the view being displayed is specified via the view GET parameter. The name of the GET parameter can be customized via viewParam. If the user doesn’t provide the GET parameter, the default view specified by defaultView will be displayed.
CActiveForm – CActiveForm provides a set of methods that can help to simplify the creation of complex and interactive HTML forms that are associated with data models.
The ‘beginWidget’ and ‘endWidget’ call of CActiveForm widget will render the open and close form tags. Most other methods of CActiveForm are wrappers of the corresponding ‘active’ methods in CHtml. Calling them in between the ‘beginWidget’ and ‘endWidget’ calls will render text labels, input fields, etc. For example, calling CActiveForm::textField would generate an input field for a specified model attribute.
CDbCriteria – CDbCriteria represents query criteria, such as conditions, ordering by, limit/offset. It can be used in AR query methods such as CActiveRecord::find and CActiveRecord::findAll.
CActiveDataProvider – CActiveDataProvider implements a data provider based on ActiveRecord.
CActiveDataProvider provides data in terms of ActiveRecord objects, which are of class modelClass. It uses the AR CActiveRecord::findAll method to retrieve the data from database. The criteria property can be used to specify various query options.
CHtml – CHtml is a static class that provides a collection of helper methods for creating HTML views.
Only text, text and text. Little boring, right! So, next I am going to start directly creating our project. So.. Get ready and sit tight.
Step-1: Database Creation
We need a database and a table for our application to store user information. So, lets create a database called ‘yiireg’ and a table named ‘users’.
CREATE DATABASE yiireg; USE yiireg; CREATE TABLE users ( id int(10) unsigned NOT NULL AUTO_INCREMENT, username varchar(20) NOT NULL, pwd_hash char(34) NOT NULL, fname varchar(64) NOT NULL, lname varchar(64) NOT NULL, email varchar(225) NOT NULL, country varchar(100) NOT NULL, address varchar(100) NOT NULL, gender varchar(20) NOT NULL, PRIMARY KEY (id), UNIQUE KEY username (username) ) ENGINE=InnoDB;From this script we have created the following fields –
id – id number of the user.
username – username for the user. In line 14 we have make this username unique.
pwd_hash – contains the encrypted password of the user. We will use PHP encrypt crypt($value) function to encrypt our password.
fname – First name of the user.
lname – Last name of the user.
email – Email address of the user.
country – Country of the user.
address – Users address.
gender – Users gender information.Step-2: Creating Yii Project and Configure main.php
Create a project with yiic application named yiireg in your htdocs folder. Now go tohttp://localhost:8080/yiireg/
If everything is ok, you will see the welcome screen of Yii.
Now, enable GiiModule from by uncommenting the following code segment from htdocs>yiireg>protected>config>main.php. And, I am setting my GiiModule password to Password.
'gii'=>array( 'class'=>'system.gii.GiiModule', 'password'=>'Password', // If removed, Gii defaults to localhost only. Edit carefully to taste. 'ipFilters'=>array('127.0.0.1','::1'), ),Uncomment the following code segment to get search engine friendly URL from main.php
'urlManager'=>array( 'urlFormat'=>'path', 'rules'=>array( '<controller:\w+>/<id:\d+>'=>'<controller>/view', '<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>', '<controller:\w+>/<action:\w+>'=>'<controller>/<action>', ), ),Comment in the SQLite database setting from main.php. Because, we will use MySQL database for our application.
/* 'db'=>array( 'connectionString' => 'sqlite:'.dirname(__FILE__).'/../data/testdrive.db', ), */Uncomment and change the MySQL database connection string from main.php by
'db'=>array( 'connectionString' => 'mysql:host=localhost;dbname=yiireg', 'emulatePrepare' => true, 'username' => 'root', 'password' => '', 'charset' => 'utf8', ),Note: Make sure you set your own username and password for your MySQL server.
Go to – http://localhost:8080/yiireg/index.php/gii. If everything is OK you will see the Yii Code Generator screen. Write the password and click Enter.
Now, we will create a model for our application with our database table. Then we will use Form Generator to create our form and lastly we will create our controller.
Step-3: Creating Model-View-Controller (MVC) with GiiModule
Let’s start now. To work with me follow the sequences.
- Click on the Model Generator link in the Yii Code Generator page. It will take us to the Model Generator page. Fill the Table Name and Model Class by our database table name.Table Name: users
Model Class: UsersThen click Preview and then Generate button. It will create a Users.php file in our htdocs>yiireg>protected>models folder.
- Click on the Form Generator link from the same page. It will open Form Generator page. Write –Model Class: Users
View Name: users/indexClick Preview and then Generate. This will create index.php file inside the views>employees folder, which will contain necessary codes for form.
It will give you a sample code segment for your controller class. For, this tutorial no needs to copy this code. However, if you work for your personal project, you may need to store this code.
- Now, click on the Controller Generator link. Fill the fields’ with-Controller ID: UsersClick Preview button. It will show an error. This is because, we have already created our views for Users controller, when we worked with Form Generator. Keep it uncheck and click Generate button. It will create UsersController.php file in our controllers folder.
Step-4: Editing Code
By this time, we have our Model, View and Controller (MVC). But these are not interlinked with each other. We have to do it by writing some code.
- Open UsersController.php file with your editor from controllers folder and replace it with following code segment.
<?php class UsersController extends Controller { public function actions() { // captcha action renders the CAPTCHA image displayed on the contact page return array( 'captcha'=>array( 'class'=>'CCaptchaAction', 'backColor'=>0xFFFFFF, ), 'page'=>array( 'class'=>'CViewAction', ), ); } public function actionIndex() { $model=new Users; if(isset($_POST['ajax']) && $_POST['ajax']==='users-index-form') { echo CActiveForm::validate($model); Yii::app()->end(); } if(isset($_POST['Users'])) { $model->attributes=$_POST['Users']; if($model->validate()) { if($model->save()) { $this->redirect(array('site/login')); } return; } } $this->render('index',array('model'=>$model)); } }Code Explanation:From line 6 to 18, we have created a method called action() which will render a CAPTCHA for our registration form.
Line 20-44, we have created a actionIndex() method which will initiated when we call Users controller.
- We need to change our views as well. So, open index.php from views>users folder with your editor and replace with the following code.
<div class="form"> <?php $form=$this->beginWidget('CActiveForm', array( 'id'=>'users-index-form', 'enableAjaxValidation'=>true, 'enableClientValidation'=>true, 'enableAjaxValidation'=>true, )); ?> <p class="note">Fields with <span class="required">*</span> are required.</p> <?php echo $form->errorSummary($model); ?> <div class="row"> <?php echo $form->labelEx($model,'username'); ?> <?php echo $form->textField($model,'username'); ?> <?php echo $form->error($model,'username'); ?> </div> <div class="row"> <?php echo $form->labelEx($model,'password'); ?> <?php echo $form->passwordField($model,'password',array('size'=>20,'maxlength'=>64)); ?> <?php echo $form->error($model,'password'); ?> </div> <div class="row"> <?php echo $form->labelEx($model,'fname'); ?> <?php echo $form->textField($model,'fname'); ?> <?php echo $form->error($model,'fname'); ?> </div> <div class="row"> <?php echo $form->labelEx($model,'lname'); ?> <?php echo $form->textField($model,'lname'); ?> <?php echo $form->error($model,'lname'); ?> </div> <div class="row"> <?php echo $form->labelEx($model,'email'); ?> <?php echo $form->textField($model,'email'); ?> <?php echo $form->error($model,'email'); ?> </div> <div class="row"> <?php echo $form->labelEx($model,'country'); ?> <?php echo $form->textField($model,'country'); ?> <?php echo $form->error($model,'country'); ?> </div> <div class="row"> <?php echo $form->labelEx($model,'address'); ?> <?php echo $form->textField($model,'address'); ?> <?php echo $form->error($model,'address'); ?> </div> <div class="row"> <?php echo $form->labelEx($model,'gender'); ?> <div class="compactRadioGroup"> <?php echo $form->radioButtonList($model,'gender',array('Male'=>'Male', 'Female'=>'Female'),array('separator'=>' '));?> </div> <?php echo $form->error($model,'gender'); ?> </div> <?php if(CCaptcha::checkRequirements()): ?> <div class="row"> <?php echo $form->labelEx($model,'verifyCode'); ?> <div> <?php $this->widget('CCaptcha'); ?> <?php echo $form->textField($model,'verifyCode'); ?> </div> <div class="hint">Please enter the letters as they are shown in the image above. <br/>Letters are not case-sensitive.</div> <?php echo $form->error($model,'verifyCode'); ?> </div> <?php endif; ?> <div class="row buttons"> <?php echo CHtml::submitButton('Submit'); ?> </div> <?php $this->endWidget(); ?> </div><!-- form -->From line 3 – 8, we have initialized CActiveForm class with a widget.
From line 10 – 62, we have used different methods from CActiveForm class to create our form fields and labels.
Line 64 – 75, we have created our CAPTCHA.
- For our model class open Users.php from models folder. Replace the existing code with following code.
<?php class Users extends CActiveRecord { public $password; public $verifyCode; public function tableName() { return 'users'; } public function rules() { return array( array('username', 'unique'), array('username, gender', 'length', 'max'=>20), array('password', 'length', 'max'=>32), array('fname, lname', 'length', 'max'=>64), array('email', 'email'), array('country, address', 'length', 'max'=>100), // verifyCode needs to be entered correctly array('verifyCode', 'captcha', 'allowEmpty'=>!CCaptcha::checkRequirements()), // The following rule is used by search(). array('id, username, pwd_hash, fname, lname, email, country, address, gender', 'safe', 'on'=>'search'), ); } /** * @return array relational rules. */ public function relations() { // NOTE: you may need to adjust the relation name and the related // class name for the relations automatically generated below. return array( ); } /** * @return array customized attribute labels (name=>label) */ public function attributeLabels() { return array( 'id' => 'ID', 'username' => 'Username', 'password' => 'Password', 'fname' => 'First Name', 'lname' => 'Last Name', 'email' => 'Email', 'country' => 'Country', 'address' => 'Address', 'gender' => 'Gender', 'verifyCode'=>'Verification Code', ); } public function search() { $criteria=new CDbCriteria; $criteria->compare('id',$this->id,true); $criteria->compare('username',$this->username,true); $criteria->compare('pwd_hash',$this->pwd_hash,true); $criteria->compare('fname',$this->fname,true); $criteria->compare('lname',$this->lname,true); $criteria->compare('email',$this->email,true); $criteria->compare('country',$this->country,true); $criteria->compare('address',$this->address,true); $criteria->compare('gender',$this->gender,true); return new CActiveDataProvider($this, array( 'criteria'=>$criteria, )); } public static function model($className=__CLASS__) { return parent::model($className); } //Save encripted password public function hash($value){ return crypt($value); } //calling hash to encrypt given password protected function beforeSave(){ if(parent::beforeSave()){ $this->pwd_hash = $this->hash($this->password); return true; } return false; } //check if the password is matched with stored encrypted password public function check($value){ $new_hash = crypt($value, $this->pwd_hash); if($new_hash == $this->pwd_hash){ return true; } return false; } }Code Explanation
From line 13 – 28, we have set rules for checking our input information.
Line 44 – 58, we rewrite our labels.
In line 86 – 88, we have created a method to encrypt our password.
From line 91 – 97, we have called our method for encryption and assigned to our pwd_hash field.
Line 100 – 106 is used to check whether the login password is matched with stored password.
- As we are using default layout. We want that register form will be visible only for the guest user. But, the logged-in user will not see the register menu option. To do so, we need to change the following code segment from views>layout>main.php file.
<div id="mainmenu"> <?php if (Yii::app()->user->isGuest) { $this->widget('zii.widgets.CMenu',array( 'activeCssClass' => 'active', 'activateParents' => true, 'items'=>array( array('label'=>'Home', 'url'=>array('/site/index')), array('label'=>'Register', 'url'=>array('/users/index')), array('label'=>'Login', 'url'=>array('/site/login')), ), )); } else { $this->widget('zii.widgets.CMenu',array( 'activeCssClass' => 'active', 'activateParents' => true, 'items'=>array( array('label'=>'Home', 'url'=>array('/site/index')), array('label'=>'Logout ('.Yii::app()->user->name.')', 'url'=>array('/site/logout')) ), )); } ?> </div><!-- mainmenu -->From line 4 – 23, we have created a menu link in the menu bar and linked it to the protected>views>users>index.php. We have checked whether the user is guest user or register user. If the visitor is guest, then Register menu will be visible otherwise not.
- We are almost done. When a visitor come to our site and want to login through the login page, the user name and password need to be checked. By default demo/demo or admin/admin is our username and password. We need to change it. So, open UserIdentity.php file from protected>components folder. Edit the authenticate() method as follows.
public function authenticate() { $users = Users::model()->findByAttributes(array('username'=>$this->username)); if($users === null) { $this->errorCode=self::ERROR_USERNAME_INVALID; }else { if($users->check($this->password)) { $this->errorCode=self::ERROR_NONE; }else{ $this->errorCode=self::ERROR_PASSWORD_INVALID; } } return !$this->errorCode; }This code will generate error messages, if the username or password does not match with our database.
- Lastly, open protected>views>site>login.php with your editor and delete the following code segment. Because, we are not using demo/demo or admin/admin as username and password.
<p class="hint"> Hint: You may login with <kbd>demo</kbd>/<kbd>demo</kbd> or <kbd>admin</kbd>/<kbd>admin</kbd>. </p>Victory, victory..
Now, open your browser and go to –
http://localhost:8080/yiireg/index.php
Click on the Register menu item and register. I tried couple of time with wrong information.
You are done with registration. Now try to login.
After successfully login, you will get the following screen. Here we are not getting the Register menu option any more. Because, we are loged in.
Conclusion
Hah.. We have done enough coding today. But we have done something very useful. Isn’t it!!!Happy Coding and Happy New Year.
Hej, I’m from Bangladesh. Learning programming is one of the freaking decisions I have taken in my life. Because, it makes me and my life crazy. I have great weakness on open source technologies. Perhaps, that’s why I do not know any closed source language. I fall in love with programming, when I started my undergraduate in East West University. Till now, I can not live without it.
Very nice post. I just stumbled upon your blog and wanted to say that I’ve truly enjoyed browsing your blog posts. After all I’ll be subscribing to your rss feed and I hope you write again very soon!
Thanks, for your compliment. 🙂
really nice!
Excellent article but for me login is not working.
Very good tuts dear. keep it up.
Shahjalal you wrote “Learning programming is one of the freaking decisions I have taken in my life. Because, it makes me and my life crazy” why is it freaking decision and why does it make your life crazy?
You started MBA because you are going to quiet it?
By the way thank you for working registration system!
there’s an error on Register 🙁
“crypt(): No salt parameter was specified. You must use a randomly generated salt and a strong hash function to produce a secure hash” ;( PLEASE HELP.
Fantastic tutorial bro, thank you very much, but my login is not working, if you’re help me it would be just great.All the way thank you.
nice tutorial, goodluck
Hello,
This seems to be a good article. Can you please elaborate steps for Step-2: Creating Yii Project and Configure main.php
Thanks
Nice
Nice blog tutorial keep it up.
thank you very much sir for your code…
im BSc computing degree student.so i have yii assigmnt
please upload how to insert image slide by stepe in to yii home page
Howdy I am so thrilled I found your site, I really found you by error, while I was searching on Google for something else, Regardless I am here now and would just like to say thank you for a remarkable post and a all round interesting blog (I also love the theme/design), I don’t have time to read it all at the moment but I have saved it and also included your RSS feeds, so when I have time I will be back to read much more, Please do keep up the excellent job.
At this time I am ready to do my breakfast, after having my breakfast coming yet again to read additional news.