Posts Tagged ‘CakePHP’

User Registration and Login With CakePHP 1.2.x

Saturday, February 20th, 2010 by Phillip Napieralski

I’m going to create a simple user registration form with a confirm password field!

Features of this implementation:

  • Uses AuthComponent
  • Confirm password field
  • Ensures unique usernames
  • User feedback if any errors occur
  • Remember me feature (cookies)

Prerequisites

I assume you have a database structure similar to the following:

Users and Groups tables

Note: CakePHP automagically updates the created and modified fields.

App Controller

Place the following code in /app/controllers/app_controller.php

<?php

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

	function beforeFilter() {
		$this->Auth->logoutRedirect =  '/';
		$this->Auth->loginRedirect = '/';
		$this->RememberMe->check();
	}
}
?>

The two lines $this->Auth->log*** determine where the user is directed after they login. Another option (instead of ‘/’), is $this->referer(). That will bring the user back to the page they were just at (sweet)!

What’s this remember me crap though?

Remember Me

This guy wrote an excellent component that you can simply pop in your /app/controllers/components folder and you’re ready to go.

The Form

Place the following code in /app/views/users/register.ctp

<?php
	echo $form->create('User', array( 'url' => array( 'action' => 'register' ) ) );
	echo $form->input('username',
					 	array('after' => $form->error('username_unique', 'The username is taken. Do try again!' ) ) );
	echo $form->input('password');
	echo $form->input('confirm_password', array( 'type' => 'password' ) );
	echo $form->input('email');
	echo $form->end('Sign-up');
?>

The only tricky piece right now should be in the username piece. In the second parameter, I specified ‘after’ that associates with a $form->error(…). The username_unique property is something I will define in the User Model shortly.

Users Controller

Place the following code in /app/controllers/users_controller.php inside your UsersController class.

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

		// This allows our login() function to execute
		$this->Auth->autoRedirect = false;
	}

	// Note, this function is called AFTER Auth has handled it, but BEFORE redirecting to the afterLogin redirect specified (because of the flag we set in beforeFilter)
	// Basically, this means the password field is already encrypted and the session was already created. Easy! =)
	function login()
	{
		// Check if Auth got a hold of it and created a session
		if( $this->Auth->user() )
		{
			// If form data was sent, set the remember_me cookie IF they checked the box
			if( isset( $this->data ) )
			{
				if( empty( $this->data['User']['remember_me'] ) || $this->data['User']['remember_me'] == 0 )
				{
					$this->RememberMe->delete();
				}
				else
				{
					$this->RememberMe->remember(
								$this->data['User']['username'],
								$this->data['User']['password'] );
				}

				$this->Session->setFlash('You are logged in!');
			}

			// Redirect to our loginRedirect page specified
			$this->redirect($this->Auth->redirect());
		}
	}

	function logout(){
		// Set logout message and call auth component's logout
		$this->Session->setFlash('You are logged out! Good bye!');

		// Delete the cookie
		$this->RememberMe->delete();

		// Call Auth to delete our session
		$this->redirect($this->Auth->logout());
	}

	function register() {
		if ( isset($this->data) )
		{
			// Set group to regular user
			//    NOTE: This may be different for you
			$this->data['User']['group_id'] = 2;

			$this->User->create();
			if ($this->User->save($this->data))
			{
				$this->Session->setFlash( 'Thank you for registering!' );
				$this->redirect(array('action'=>'index'));
			}
			else
			{
				// Make the password fields blank
				unset($this->data['User']['password']);
				unset($this->data['User']['confirm_password']);

				$this->Session->setFlash('An error occurred, try again!');
			}
		}
	}

Excellent! Now we just have one more step… let’s validate the user’s input.

The User Model

Place the following code in /app/models/user.php

<?php
class User extends AppModel {

	var $name = 'User';

	// Prevent the input from having an id
	// Username: MUST be alphanumeric
	// Password: We specify a function that returns a bool value
	var $validate = array(
		'id' => array(
							'rule' => 'blank',
							'on' => 'create'
					 ),
		'username' => array(
							'rule' => 'alphanumeric',
							'message' => 'Please enter a valid username',
							'required' => true
						),
		'password' => array(
							'rule' => array('confirmPassword', 'password'),
							'message' => 'Passwords do not match',
							'required' => 'true'
						),
		'confirm_password' => array(
							'rule' => 'alphanumeric',
							'required' => 'true'
						),
		'email' => array( 'email',
							array( 'rule' => array('email'),
									'message' => 'Please enter a valid email!' )
						),
		'group_id' => array( 'numeric' )
	);

	function confirmPassword($data)
	{
		// We must manually hash the second piece in the same way the AuthComponent would
		// if they match, return true!
		if ($data['password'] == Security::hash(Configure::read('Security.salt') . $this->data['User']['confirm_password'])) {
			return true;
		}

		// hashed passwords did NOT match
		return false;
	}

	// Check if the username already exists by doing SELECT COUNT(*) FROM users WHERE username = 'your_username'
	function beforeValidate()
	{
		if( !$this->id )
		{
			if( $this->findCount( array('User.username' => $this->data['User']['username'] ) ) > 0 )
			{
				// If any rows are found, send an error and call it 'username_unique'
				// In our view, we can check for this by doing $form->error('username_unique','Not Unique Username!!!')
				//   As specified in the view code I placed above
				$this->invalidate('username_unique');
				return false;
			}
		}
		return true;
	}

	// Associations
	var $belongsTo = 'Group';
}
?>

Great!

Conclusion

There you have it. Something that may take you places in your projects (hopefully). Any questions?

CakePHP Cheatsheets

Friday, February 19th, 2010 by Phillip Napieralski

This is a list of cheatsheets and links for things that I commonly use (and forget the syntax to) in CakePHP.

Very Simple User Authentication with CakePHP

Monday, February 15th, 2010 by Phillip Napieralski

DISCLAIMER: I created this super simple user authentication post for CakePHP version 1.2.7. The latest versions of CakePHP (v2+) have been heavily enhanced. As a side effect, the code in this post will no longer work out-of-the-box. However, I’ll continue to leave this post up as a learning tool. Happy coding!

I’m working with my Software Engineering II class to build an online survey for United Way. I was left in charge to create a simple way to login and authenticate.

The following implementation uses sessions exclusively. I built it so you MUST log-in before accessing any part of the site/survey. You could easily build off this, or even switch to Cake’s built-in ACL and Auth features.

Preliminaries

The database structure presented is the same as in my previous post about a user account class. Here it is again however:

The Login Form View

The login form is extremely simple using Cake’s built in form helper. Here’s how we do it:

<?php
// Pass the model name into the Create function, also pass where the data will be sent
echo $form->create('User', array( 'controller' => 'users', 'action' => 'login' ) );

// Cake automatically knows, based on the input, to create input fields for these two. Cool eh?
echo $form->input('username');
echo $form->input('password');

// Create the submit button
echo $form->end('Login');
?>

That’s much less code, and way clearer once you get used to it. I placed this file in /app/views/users/login_form.ctp

The User Model

<?php
class User extends AppModel {
	function validateLogin($data)
	{
		// Search our database where the 'username' field is equal to our form input.
		// Same with the password (this example uses PLAIN TEXT passwords, you should encrypt yours!)
		// The second parameter tells us which fields to return from the database
		// Here is the corresponding query:
		// "SELECT id, username FROM users WHERE username = 'xxx' AND password = 'yyy'"
		$user = $this->find(array('username' => $data['username'], 'password' => $data['password']), array('id', 'username'));

		if( empty($user) == false )
		{
			return $user;
		}

		return false;
	}
}
?>

This code should go in /app/models/user.php

The Users Controller

<?php
class UsersController extends AppController {
	var $name = 'Users';
	var $helpers = array('Form');

	// Placeholder for login_form, required by CakePHP to see the login_form view
	function login_form() { }

	function login() {
		// Check if they went here after submitting the form
		// Note that all our form data is preceded by the model name ['User']
		if(empty($this->data['User']['username']) == false)
		{
			// Here we validate the user by calling that method from the User model
			if(($user = $this->User->validateLogin($this->data['User'])) != false)
			{
				// Write some Session variables and redirect to our next page!
				$this->Session->setFlash('Thank you for logging in!');
				$this->Session->write('User', $user);

				// Go to our first destination!
				$this->Redirect(array('controller' => 'Controller_name', 'action' => 'Action_name'));
				exit();
			}
			else
			{
				$this->Session->setFlash('Incorrect username/password!', true);
				$this->Redirect(array('action' => 'login_form'));
				exit();
			}
		}
	}

	function logout() {

		$this->Session->destroy();
		$this->Session->setFlash('You have been logged out!');

		// Go home!
		$this->Redirect('/');
		exit();
	}
}
?>

Place this code in /app/controllers/users_controller.php

The AppController – Validate on Every Page

class AppController extends Controller {

	// Check if they are logged in
	function authenticate()
	{
		// Check if the session variable User exists, redirect to loginform if not
		if(!$this->Session->check('User'))
		{
			$this->redirect(array('controller' => 'users', 'action' => 'login_form'));
			exit();
		}
	}

	// Authenticate on every action, except the login form
	function afterFilter()
	{
		if( $this->action != 'login_form' )
		{
			$this->authenticate();
		}
	}
}

Now simply place this code in /app/app_controller.php

What does this do? Well, now no matter where anyone goes on your website, they will be redirected to the login page UNLESS they are logged in! Excellent.