8 min read

(For more resources related to this topic, see here.)

Mission Checklist

This project assumes that you have a web development environment prepared. The files for this project include a Yii project directory with a database schema. To prepare for the project, carry out the following steps replacing the username lomeara with your own username:

  1. Copy the project files into your working directory.

    cp –r ~/Downloads/project_files/Chapter 3/project_files ~/projects/ch3

  2. Make the directories that Yii uses web writeable.

    cd ~/projects/ch3/ sudo chown -R lomeara:www-data protected/runtime assets protected/models protected/controllers protected/views

  3. If you have a link for a previous project, remove it from the webroot directory.

    rm /opt/lampp/htdocs/cddb

  4. Create a link in the webroot directory to the copied directory.

    cd /opt/lampp/htdocs sudo ln -s ~/projects/ch3 cbdb

  5. Import the project into NetBeans (remember to set the project URL to http://localhost/cbdb) and configure for Yii development with PHPUnit.
  6. Create a database named cbdb and load the database schema (~/projects/ch3/ protected/data/schema.sql) into it.
  7. If you are not using the XAMPP stack or if your access to MySQL is password protected, you should review and update the Yii configuration file (in NetBeans it is ch3/Source Files/protected/config/main.php).

Adding a User Object with CRUD

As a foundation for our user management system, we will add a User table to the database and then use Gii to build a quick functional interface.

Engage Thrusters

    1. Let’s set the first building block by adding a User table containing the following information:
      • A username
      • Password hash
      • Reference to a person entry for first name and last name

      In NetBeans, open a SQL Command window for the cbdb database and run the following command:

      CREATE TABLE ‘user’ ( ‘id’ int(10) unsigned NOT NULL AUTO_INCREMENT, ‘username’ varchar(20) NOT NULL, ‘pwd_hash’ char(34) NOT NULL, ‘person_id’ int(10) unsigned NOT NULL, PRIMARY KEY (‘id’), UNIQUE KEY ‘username’ (‘username’), CONSTRAINT ‘userperson_ibfk_2’ FOREIGN KEY (‘person_id’) REFERENCES ‘person’ (‘id’) ON DELETE CASCADE ) ENGINE=InnoDB;

      
      
    2. Open a web browser to the Gii URL http://localhost/cbdb/index.php/gii(the password configured in the sample code is yiibook) and use Gii to generate a model from the user table.
    3. Then, use Gii to generate CRUD from the user model.
    4. Back in NetBeans, add a link to the user index in your site’s logged in menu (ch3 | Source Files | protected | views | layouts | main.php). It should look like this:

      } else { $this->widget(‘zii.widgets.CMenu’,array( ‘activeCssClass’ => ‘active’, ‘activateParents’ => true, ‘items’=>array( array(‘label’=>’Home’, ‘url’=>array(‘/site/index’)), array(‘label’=>’Comic Books’, ‘url’=>array(‘/book’), ‘items’ => array( array(‘label’=>’Publishers’, ‘url’=>array(‘/publisher’)), ) ), array(‘label’=>’Users’, ‘url’=>array(‘/user/index’)), array(‘label’=>’Logout (‘.Yii::app()->user- >name.’)’, ‘url’=>array(‘/site/logout’)) ), )); } ?>

      
      
    5. Right-click on the project name, run the site, and log in with the default username and password (admin/admin). You will see a menu that includes a link named Users.

    6. If you click on the Users link in the menu and then click on Create User, you will see a pretty awful-looking user-creation screen. We are going to fix that. First, we will update the user form to include fields for first name, last name, password, and repeat password. Edit ch3 | Source Files | protected | views | user | _form.php and add those fields.
    7. Start by changing all instances of $model to $user. Then, add a call to errorSummary on the person data under the errorSummary call on user.

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

      
      
    8. Add rows for first name and last name at the beginning of the form.

      <div class=”row”> <?php echo $form->labelEx($person,’fname’); ?> <?php echo $form->textField($person,’fname’,array (‘size’=>20,’maxlength’=>20)); ?> <?php echo $form->error($person,’fname’); ?> </div> <div class=”row”> <?php echo $form->labelEx($person,’lname’); ?> <?php echo $form->textField($person,’lname’,array (‘size’=>20,’maxlength’=>20)); ?> <?php echo $form->error($person,’lname’); ?> </div>

      
      
    9. Replace the pwd_hash row with the following two rows:

      <div class=”row”> <?php echo $form->labelEx($user,’password’); ?> <?php echo $form->passwordField($user,’password’,array (‘size’=>20,’maxlength’=>64)); ?> <?php echo $form->error($user,’password’); ?> </div> <div class=”row”> <?php echo $form->labelEx($user,’password_repeat’); ?> <?php echo $form->passwordField($user,’password_repeat’,array (‘size’=>20,’maxlength’=>64)); ?> <?php echo $form->error($user,’password_repeat’); ?> </div>

      
      
    10. Finally, remove the row for person_id.
    11. These changes are going to completely break the User create/update form for the time being.

      We want to capture the password data and ultimately make a hash out of it to store securely in the database. To collect the form inputs, we will add password fields to the User model that do not correspond to values in the database. Edit the User model ch3 | Source Files | protected | models | User.php and add two public variables to the class:

      class User extends CActiveRecord { public $password; public $password_repeat;

      
      
    12. In the same User model file, modify the attribute labels function to include labels for the new password fields.

      public function attributeLabels() { return array( ‘id’ => ‘ID’, ‘username’ => ‘Username’, ‘password’ => ‘Password’, ‘password_repeat’ => ‘Password Repeat’ ); }

      
      
    13. In the same User model file, update the rules function with the following rules:
      • Require username
      • Limit length of username and password
      • Compare password with password repeat
      • Accept only safe values for username and password

      We will come back to this and improve it, but for now, it should look like the following:

      public function rules() { // NOTE: you should only define rules for those attributes //that will receive user inputs. return array( array(‘username’, ‘required’), array(‘username’, ‘length’, ‘max’=>20), array(‘password’, ‘length’, ‘max’=>32), array(‘password’, ‘compare’), array(‘password_repeat’, ‘safe’), ); }

      
      
    14. In order to store the user’s first and last name, we must change the Create action in the User controller ch3 | Source Files | protected | controllers | UserController. php to create a Person object in addition to a User object.

      Change the variable name $model to $user, and add an instance of the Person model.

      public function actionCreate() { $user=new User; $person=new Person; // Uncomment the following line if AJAX validation is //needed // $this->performAjaxValidation($user); if(isset($_POST[‘User’])) { $user->attributes=$_POST[‘User’]; if($user->save()) $this->redirect(array(‘view’,’id’=>$user->id)); } $this->render(‘create’,array( ‘user’=>$user, ‘person’=>$person, )); }

      
      
    15. Don’t reload the create user page yet. First, update the last line of the User Create view ch3 | Source Files | protected | views | user | create.php to send a User object and a Person object.

      <?php echo $this->renderPartial(‘_form’, array(‘user’=>$user, ‘person’ =>$person)); ?>

      
      
    16. Make a change to the attributeLabels function in the Person model (ch3 | Source Files | protected | models | Person.php) to display clearer labels for first name and last name.

      public function attributeLabels() { return array( ‘id’ => ‘ID’, ‘fname’ => ‘First Name’, ‘lname’ => ‘Last Name’, ); }

      
      

The resulting user form should look like this:

  1. Looks pretty good, but if you try to submit the form, you will receive an error. To fix this, we will change the User Create action in the User controller ch3 | Source Files | protected | controllers | UserController.php to check and save both User and Person data.

    if(isset($_POST[‘User’], $_POST[‘Person’])) { $person->attributes=$_POST[‘Person’]; if($person->save()) { $user->attributes=$_POST[‘User’]; $user->person_id = $person->id; if($user->save()) $this->redirect(array(‘view’,’id’=>$user->id)); } }

    
    
  2. Great! Now you can create users, but if you try to edit a user entry, you see another error. This fix will require a couple of more changes.

    First, in the user controller ch3 | Source Files | protected | controllers | UserController.php, change the loadModel function to load the user model with its related person information:

    $model=User::model() ->with(‘person’) ->findByPk((int)$id);

    
    
  3. Next, in the same file, change the actionUpdate function. Add a call to save the person data, if the user save succeeds:

    if($model->save()) { $model->person->attributes=$_POST[‘Person’]; $model->person->save(); $this->redirect(array(‘view’,’id’=>$model->id)); }

    
    
  4. Then, in the user update view ch3 | Source Files | protected | views | user | update.php, add the person information to the form render.

    <?php echo $this->renderPartial(‘_form’, array(‘user’=>$model, ‘person’ => $model->person)); ?>

    
    
  5. One more piece of user management housekeeping; try deleting a user. Look in the database for the user and the person info. Oops. Didn’t clean up after itself, did it? Update the User controller ch3 | Source Files | protected | controllers | UserController.php once again. Change the call to delete in the User delete action:

    $this->loadModel($id)->person->delete();

    
    

Objective Complete – Mini Debriefing

We have added a new object, User, to our site, and associated it with the Person object to capture the user’s first and last name. Gii helped us get the basic structure of our user management function in place, and then we altered the model, view, and controller to bring the pieces together.

LEAVE A REPLY

Please enter your comment!
Please enter your name here