Introduction to CakePHP features (build an app in less than 15 minutes)

I would like to showcase the awesome power of CakePHP by providing an introductory tutorial, which is going to cover some basics as well as slightly more advanced features such as the Auth (user authorization) component.

The goal of this tutorial is to setup a working application, which is going to have: user registration, login/logout functionality, user homepage, basic access control, data (form) validation and a flexible base to expand upon. Oh yeah, we’ll get all that done in just under 15 minutes and with only about 120 lines of code…

I assume that at this point you’ve got CakePHP installed and working. It would also be helpful to know the basic concepts and possibly have tried the blog tutorial.

The steps I take to build the app may not be very intuitive for a beginner, but I do provide explanations of each feature/step and hopefully at the end you’ll see how everything comes together.

Let’s begin…

We need a users table in our database to hold the User data, here’s a sample for MySQL:

[sourcecode language=”sql”]
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(200) NOT NULL,
`email` varchar(200) NOT NULL,
`username` varchar(200) NOT NULL,
`password` varchar(200) NOT NULL,
`status` tinyint(1) NOT NULL default ‘1’,
`company_id` int(11) NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
[/cc]

Next, we’ll create a system-wide controller, otherwise known as App Controller. Think of it as a global controller, whose properties and methods are accessible to all other controllers of the application.

Go into your main app directory (something like /home/teknoid/cake/app) and create a file named app_controller.php there.

app_controller.php
This is what it should look like:

class AppController extends Controller {
    var $components = array('Auth');

    function beforeFilter() {
        $this->Auth->allow('display');
        $this->Auth->loginRedirect = array('controller'=>'users', 'action'=>'index');
    }
}

Let’s take a look at what we did here…
We know that our app is going to need user authentication, therefore we include CakePHP’s built-in component with var $components = array(‘Auth’);
We then create a special function beforeFilter(), which is going to be executed before all other actions in our application, so this is a pretty good time to tell the Auth component about some things (settings) that it should be aware of.
For example, we allow a special action called ‘display’ $this->Auth->allow(‘display’) and we also tell the Auth component that, by default, upon login a user should be redirected to the ‘index’ action (or their homepage) $this->Auth->loginRedirect = array(‘controller’=>’users’, ‘action’=>’index’);.
I do not want to go into much detail about the specifics of this setup, but overall this is a pretty basic and generic way to set-up the authentication in a CakePHP application.

Next, we are going to build our Users Controller.
Go into app/controllers and create a file named users_controller.php

users_controller.php
This is what it should look like:

class UsersController extends AppController {

        var $name = 'Users';
        var $helpers = array('Time');

        function beforeFilter() {
           parent::beforeFilter();

           if($this->action == 'add') {
               $this->Auth->authenticate = $this->User;
           }

           $this->Auth->allow('add');
        }

        function index() {
            $this->set('user', $this->User->find('first',
                                                             array(
                                                                'conditions'=>array(
                                                                     'User.id'=>$this->Auth->user('id')))));
        }

        function add() {
            if(!empty($this->data)) {
                $this->User->save($this->data);
            }
        }

        function login() {

        }

        function logout() {
            $this->redirect($this->Auth->logout());
        }

    }

Here’s what we did…
We’ve included the built-in Time Helper var $helpers = array(‘Time’);, which basically helps to format (into something human readable) the date and time fields from the database… amongst other things. Later we’ll use it to format the date/time information on our user’s homepage.

You’ve noticed that we have another beforeFilter() function as we did in the App Controller. In this example we inherit our parent (App Controller) functionality by using parent::beforeFilter() and then we specify some additional settings for Auth that will be specific to the Users Controller.

Before we procede, I’d like to point out that Auth component will automatically hash your passwords with sha1() + a security salt you’ve setup during configuration/installation of CakePHP. Be sure to make the password column in your DB long enough to account for the hashed value (plain text passwords will not be stored).

This line $this->Auth->authenticate = $this->User; might be a little unusual…
The quick explanation, as to why I did that, is because Auth component allows us to override the way it handles some things, which offers great flexibility. Personally, I had a little beef with the way Auth handles password hashing, but as you can see it only bothered me during the ‘add’ action if($this->action == ‘add’), in which case I provided my own authentication object (the User model). This is going to become a lot more clear later on (just keep it in mind for now).

To keep things secure from the get-go, we have to be very explicit about what actions we allow the user to access without having to login first. By default Auth is going to redirect the user to the login form regardless of what page one tries to access. Of course it would be really sad for the user, if we didn’t tell Auth that ‘add’ action can be accessed without login $this->Auth->allow(‘add’);.
It is important to point out that Auth will assume that ‘login’ and ‘logout’ are always accessible and you shouldn’t specify those in the list of allowed actions.

Alright, our basic setup is complete and now we’ve added some actions into our Users Controller.

index() This is going to be our user’s homepage. Remember how we told Auth that upon login the user should be redirected $this->Auth->loginRedirect = array(‘controller’=>’users’, ‘action’=>’index’);? Well, now we’ve actually got that part taken care of. The action is really simple, it finds all the information about our logged in user and sets it ready for the view.

add() No mystery here, this is our user registration action and it should be quite obvious as to what it does.

login() It is this easy to get the basic login functionality working… i.e. you don’t even need to write any code. Auth is smart enough to do the work for you. Of course you can easily change the way Auth does things by extending this action beyond the basics.

logout() Once the user clicks a “Log out” link he/she will be redirected to the default location, which happens to be back to the login form. Good enough for this example.

Now that we’ve got some of our actions finished up, let’s create the views for each one (a view is the actual page that the user is going to see in the browser).

Go to app/views/ and create a directory named ‘users’. Then in that directory add a view file for each of the actions (except logout, since it doesn’t need one).

index.ctp – our homepage

<?php echo $html->link('Log out', array('controller'=>'users', 'action'=>'logout')); ?>

<div>
    Hello, <?php echo $user['User']['name']; ?>
</div>

<div>
    Your account was created <?php echo $time->niceShort($user['User']['created']); ?>
</div>

This is a very simple view page, but it does show how you can access the various user information in the view. If you look back to our index() action in the Users Controller, you’ll see that we set a $user variable, which contains an array of user data and now we can echo it in the view.
Remember that Time Helper we’ve included earlier in our Users Controller? Well, here we’re using it to transform our database DATETIME field’s value, into a nice, human readable output by using: echo $time->niceShort($user[‘User’][‘created’]);

login.ctp – our login page

if  ($session->check('Message.auth')) $session->flash('auth');

    echo $form->create('User', array('action' => 'login'));
    echo $form->input('username');
    echo $form->input('password');
    echo $form->end('Login');

Even though we don’t need any code for the login() action we still need to setup a basic form to display to the user. Nothing fascinating here, just note that first line if ($session->check(‘Message.auth’)) $session->flash(‘auth’);, which allows us to display any messages triggered by the Auth component.

add.ctp – our registration page

    echo $form->create();
    echo $form->input('name');
    echo $form->input('email');
    echo $form->input('username');
    echo $form->input('password');
    echo $form->input('password_confirm', array('type'=>'password'));
    echo $form->end('Register');

Here we have setup a basic form for the registration. Again there is really nothing fancy here… just a basic form, which will be brilliantly built by the Form Helper.

If you haven’t tried already, go ahead now:

Try to access http://yourlocalhost/users/shouldntAccessThis
– Auth component should redirect you to the login form, since we’ve never specified that ‘shouldntAccessThis’ is allowed.

Try to access http://yourlocalhost/users/add
– You should see a registration form

So far so good, but we’ve got to setup some validation rules for our registration form…
For this we create our User Model.

In app/models/ create the file user.php

user.php
This is what it should look like:

class User extends AppModel {

        var $name = 'User';

        var $validate = array(
            'name'=>array(
                          'rule'=>array('minLength', 4),
                          'message'=>'Name has to be at least four characters'),

            'email'=>array(
                          'rule'=>array('email'),
                          'message'=>'Please enter a valid email'),

            'username'=>array(
                              'Username has to be at least four characters'=>array(
                                  'rule'=>array('minLength', 4)
                              ),
                              'This username is already taken, please try another'=>array(
                                  'rule'=>'isUnique'
                              )),

            'password'=>array(
                              'Password cannot be empty'=>array(
                                   'rule'=>array('notEmpty')
                                ),
                              'Password must be at least four characters'=>array(
                                   'rule'=>array('minLength', 4)
                                ),
                              'Passwords must match'=>array(
                                   'rule'=>array('passwordCompare', 'password_confirm')
                              ))
        );

        function passwordCompare($data, $fieldTwo) {

            if($data['password'] != $this->data[$this->alias][$fieldTwo]) {
                 $this->invalidate($fieldTwo, 'Passwords must match');
                 return false;
            }

            return true;
        }


        function hashPasswords($data, $enforce=false) {

           if($enforce && isset($this->data[$this->alias]['password'])) {
              if(!empty($this->data[$this->alias]['password'])) {
                  $this->data[$this->alias]['password'] = Security::hash($this->data[$this->alias]['password'], null, true);
               }
           }

           return $data;
        }

        function beforeSave() {
            $this->hashPasswords(null, true);

            return true;
        }
    }

So this is the big guy. The User Model handles our validation logic and also overrides some of the default Auth component functionality. Remember how I said that we’ll use the User model to override default Auth behavior $this->Auth->authenticate = $this->User; during the ‘add’ action? Well here I have built my own hashPasswords() method, which overrides the Auth component’s default one. Without going into a long winded explanation I’ll just say that I prefer to hash the passwords just before saving the data (hence the little beforeSave() method) because it makes my life easier, and it also shows how easily Auth can be extended to behave the way you want it to.

Let’s take a look at passwordCompare(). This is my custom validation method to compare ‘password’ and ‘password_confirm’ fields (look back to our add.ctp view).
$this->invalidate($fieldTwo, ‘Passwords must match’); will cause the ‘password_confirm’ field to be marked as invalid in addition to the ‘password’ field. This is nice if you’ve got some CSS that, perhaps, puts a red border on the input box for the invalid field (notice that we’ve also set the error message).
Another trick (I’m not quite sure honestly if it’s a documented feature) is to use really nice, human readable names for a given rule. This is in case you use multiple rules per field as we do for our ‘password’ field. This way, in case of an error, the arbitrary rule name will be used for the error message.

Go ahead, try it out with some invalid and then valid data… If all goes well, you should see an SQL debug showing the inserted user data, which confirms that your form is working correctly.

And there you have it… A complete, functional app in about 10-12 minutes.

P.S. I have posted a follow-up to this tutorial, which covers some of the Auth features used here in more detail.

Related Posts

%d bloggers like this: