Cook your own User Authentication in Yii – Part 3

This is the last but one in a series of 4 tutorials demonstrating how to implement a simple role based user administration system in Yii.  

The first two parts can be found here;  user authentication – part 1 and user authentication – part 2

Previously

We’ve update the the UserIdentity class which was originally generated by Gii, to add database authentication.
We then added a new class called WebUser which stores the logged in user details, such as name, email, role etc…

Moving Forward

In this tutorial we are going to create the Controller and CRUD operations for users to be able to update their passwords and profiles.
In Part 4 we will add a sign-up process including emailing an email address confirmation with a token.
So if you haven’t already created the controller and views (be careful not to over-write the User model though) then go to Gii and create these.
Now we got some forms and basic controller actions to handle the profile updates but let’s tidy up the users _form form as follows:-

<!--?php
/* @var $this UsersController */
/* @var $model Users */
/* @var $form CActiveForm */
?---->
<div class="form">
<!--?php $form=$this->beginWidget('CActiveForm', array(
 'id'=>'users-form',
 'enableAjaxValidation'=>true,
)); ?>
 <p class="note">Fields with <span class="required">*</span> are required.</p>
 <!--?php 
            foreach (Yii::app()->user->getFlashes() as $type=>$flash) {
                echo "<div class="{$type}">{$flash}</div>";
            }
        ?>
 <!--?php echo $form->errorSummary($model); ?>
 <div class="block">
     <!--?php if ($model->isNewRecord) { ?>
     <div class="row">
     <!--?php echo $form->labelEx($model,'username'); ?>
     <!--?php echo $form->textField($model,'username',array('size'=>60,'maxlength'=>128)); ?>
     <br />
     <!--?php echo $form->error($model,'username'); ?>
     </div>
     <!--?php } ?---->
     <div class="row">
     <!--?php echo $form->labelEx($model,'email'); ?>
     <!--?php echo $form->textField($model,'email',array('size'=>60,'maxlength'=>128)); ?>
     <br />
     <!--?php echo $form->error($model,'email'); ?>
     </div>
     <div class="row">
     <!--?php echo $form->labelEx($model,'firstname'); ?>
     <!--?php echo $form->textField($model,'firstname',array('size'=>60,'maxlength'=>128)); ?>
     <br />
     <!--?php echo $form->error($model,'firstname'); ?>
     </div>
     <div class="row">
     <!--?php echo $form->labelEx($model,'lastname'); ?>
     <!--?php echo $form->textField($model,'lastname',array('size'=>60,'maxlength'=>128)); ?>
     <br />
     <!--?php echo $form->error($model,'lastname'); ?>
     </div>
     <div class="row">
     <!--?php echo $form->labelEx($model,'pagination'); ?>
     <!--?php echo $form->textField($model,'pagination'); ?>
     <br />
     <!--?php echo $form->error($model,'pagination'); ?>
     </div>
     <!--?php if ($model->isNewRecord) { ?>
     <div class="row">
     <!--?php echo $form->labelEx($model,'role'); ?>
     <!--?php echo $form->textField($model,'role'); ?>
     <br />
     <!--?php echo $form->error($model,'role'); ?>
     </div>
     <!--?php } ?---->
 </div>
 <div class="block">
     <div class="row">
     <!--?php echo $form->labelEx($model,'passwordSave'); ?>
     <!--?php echo $form->passwordField($model,'passwordSave',array('size'=>60,'maxlength'=>256)); ?>
     <br />
     <!--?php echo $form->error($model,'passwordSave'); ?>
     </div>
     <div class="row">
     <!--?php echo $form->labelEx($model,'repeatPassword'); ?>
     <!--?php echo $form->passwordField($model,'repeatPassword',array('size'=>60,'maxlength'=>256)); ?>
     <br />
     <!--?php echo $form->error($model,'repeatPassword'); ?>
     </div>
     <div class="row buttons">
     <!--?php echo CHtml::submitButton($model->isNewRecord ? 'Create' : 'Save'); ?>
     </div>
 </div>
 <br class="clear" />
 <div class="row buttons">
 <!--?php echo CHtml::submitButton($model->isNewRecord ? 'Create' : 'Save'); ?>
 </div>
<!--?php $this->endWidget(); ?>
</div><!-- form --></div></div></div>


You might notice that I’m using a style called block which is as follows:-
div.block {
    float:left;
    padding-right: 20px;
}


So we should now have a form looking something like this:-

Now this form is slightly different, in that the password fields are not using the standard model attributes but two public attributes called passwordSave and repeatPassword and thus only update the model->password if the password has been changed.
Look in the model beforeSave event and you will see that it checks that passwordSave is !empty and then updates the database password with an MD5 of the entered password.

Note: this is not that secure and for a production environment it is often recommended that you use user salts!

Now, in my controller, I prefer that the user stays in the Update screen after clicking save, so I use Flash messages to tell the user that the update was successful (or not!)

My update action therefore looks like this:-

public function actionUpdate($id)
 {
 $model=$this->loadModel($id);
 // Uncomment the following line if AJAX validation is needed
 $this->performAjaxValidation($model);
 if(isset($_POST['Users']))
 {
 $model->attributes=$_POST['Users'];
 if($model->save()) {
     Yii::app()->user->setFlash('saved', "Data saved!");
                        } else {
                            Yii::app()->user->setFlash('failure', "Data Not saved!");
                        }
 }
 $this->render('update',array(
 'model'=>$model,
 ));
 }

User Menu

I prefer to separate out the user functions from the Business functions in the top menu more like this:-

This is created in the views/layouts/main.php or if the them that you are using has a main.php then this will over-ride the it.

So this part of my main.php now looks like this:-

 
  <div id="header">
    <div id="logo">
     <!--?php echo CHtml::encode(Yii::app()->name); ?></div>
    <!--?php /**CB-6.2**/ ?---->
    <div id="login">
        <!--?php 
      if (Yii::app()->user->isGuest) {
        $userMenu=array(
          array('label'=>'Login', 'url'=>array('/site/login'), 'visible'=>Yii::app()->user->isGuest)
          );
      } else {
          $userMenu=array(
        array('label'=>'Welcome '.Yii::app()->user->firstname),
        array('label'=>'Settings', 'url'=>array('/users/update','id'=>Yii::app()->user->id)),
        array('label'=>'Logout ('.Yii::app()->user->firstname.')', 'url'=>array('/site/logout'))
        );
      }
      $this->widget('zii.widgets.CMenu',array(
          'items'=>$userMenu,
          'htmlOptions'=>array('class'=>'userMenu')
      ));
      ?>
    </div>
      <!--?php ?---->
  </div><!-- header -->
  <div id="mainmenu">
    <!--?php $this->widget('zii.widgets.CMenu',array(
      'items'=>array(
        array('label'=>'Home', 'url'=>array('/album/index')), /**CB-6.2**/
        array('label'=>'About', 'url'=>array('/site/page', 'view'=>'about')),
        array('label'=>'Contact', 'url'=>array('/site/contact')),
        //array('label'=>'Login', 'url'=>array('/site/login'), 'visible'=>Yii::app()->user->isGuest),
        //array('label'=>'Logout ('.Yii::app()->user->name.')', 'url'=>array('/site/logout'), 'visible'=>!Yii::app()->user->isGuest)
      ),
    )); 
    ?>
  </div><!-- mainmenu -->
</div></div></div>


and then we need to make this pretty with a bit of Css.
#header {
    position: relative;
    margin: 0;
    padding: 0;
    border-top: 3px solid #749199;
}
#login {
    position: absolute;
    top: 0px;
    right: 0px;
    -moz-border-radius: 0px 0px 5px 5px;
    border-radius:  0px 0px 5px 5px;
    background: #749199;
    padding: 0 10px;
    color: #DBFFED;
    font-size: 10px;
}
#login a {
    color: #DBFFED;
    font-weight: bold;
}
#login ul.userMenu li a:hover {
   color: #71FFCF 
}
#mainmenu {
    background: #DBFFED repeat-x left top;
}
/**user menu**/
ul.userMenu {
    margin: 0;
}
ul.userMenu li {
    line-height: 16px;
    float: left;
    list-style: none;
    padding: 0 5px 0px 5px;
    border-right: 1px dashed #EBF1E8 !important;   
}
#mainmenu ul li a {
    color: #749199;
}
#mainmenu ul li a:hover, #mainmenu ul li.active a {
color: #6C3108;
background-color: #CC839C;
text-decoration: none;
}
a {
    color: #51B2CC
}


OK, that’s probably enough for today, so I’m going to add another tutorial after this to add the sign-up function.

Please note, that I did go back and make one or two small changes to the model, so if you’ve already created that in your application go back to lesson 1 and check for the changes…

Let’s Start a Project!

Contact Me