http://www.developer.com/

Back to article

Managing User Accounts with the Zend Framework


November 16, 2009

Managing User Accounts with the Zend Framework - Introduction

Today's consumer sure has become the discerning sort, hasn't he? No longer content with a standard product made for the masses, today's buyer seeks out wares capable of being modified to fit specific tastes and desires. iPod engraving, customized Scions, and even monogrammed postage stamps are just a few examples of the lengths companies (and consumers) are going to, to make the marketplace their very own.

With the Web a crucial part of our everyday lives, not to mention a major part of the societal marketplace, it doesn't come as a surprise that consumers have come to expect as much from the virtual world as they do of the physical. Custom sports scores, localized news, and tailor-made product catalogs are all typical features on today's web sites. Of course, barring the employment of The Amazing Kreskin, Web site developers require a way to match a user to his online preferences. The standard way to do so is by providing the user with a means for creating and logging into an account. This account serves as the glue which ties the user to his actions performed while navigating the Web site, such as purchasing an e-book, identifying his hometown as Columbus, Ohio, or specifying that he'd like to see only sports-related news hailing from Pittsburgh.

Creating a user account feature is a bit more involved than simply creating a database table for hosting account information, and plugging it into the requisite registration and login forms. You'll also need to think about safeguarding against bogus accounts by requiring the user to confirm his e-mail address before the account is activated, maintaining the user's session while he's navigating the Web site, providing a simple mechanism for logging out of an account, and allowing users to easily recover forgotten passwords. Recognizing the commonplace need for such features, the Zend Framework is bundled with a great component named Zend_Auth which significantly reduces the time required to create and manage user accounts. In this tutorial I'll show you how to create the building blocks for managing user accounts, showing you how to register users, and allow them to both login and logout of your Web site.

Creating the User Registration Feature

In order to create a user registration feature, we'll need a place to store the account information. The most logical place to do so is within a database table specially designated for this purpose. A sample MySQL table schema for such a purpose might look like this:

CREATE TABLE account {
 id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
 first_name VARCHAR(100) NOT NULL,
 last_name VARCHAR(100) NOT NULL,
 email VARCHAR(100) NOT NULL,
 pswd CHAR(32) NOT NULL,
 recovery_key CHAR(32) DEFAULT ""
}

Nothing in this table should be too surprising, except for perhaps the pswd and recovery_key columns. The pswd column is defined as a CHAR(32) because the user's password will always be stored in hashed format using PHP's md5() function, making it highly unlikely the plaintext password will be recovered should an attacker somehow gain access to the database. The md5() function will convert any string to a 32-character hash, giving reason to the type definition. The recovery_key column will be used later in this tutorial to hold a random 32-character string which will be used to confirm the user's identity in case he initiates the password recovery sequence. I'll talk more about this particular column in a bit.

Of course, you'll probably need to extend the account table to include other pertinent information, such as the user's address, phone number, or birthdate. For the purposes of this tutorial I'm focusing on just the bare minimum requirements, so don't be afraid to add to this table as you see fit.

The Account Model

Next you'll need to create the account model pursuant to the typical convention defined by the Zend Framework. I'm going to keep the initial model capabilities to a minimum, although we'll add to it as necessary.

class Default_Model_Customer extends Zend_Db_Table_Abstract
{
  protected $_name = 'customer';
}

The Account Registration Form

Next up is the registration form. I'm going to keep this to the minimum required fields, creating the form you see in Figure 1. As there's nothing special about this form, following the screenshot I'll skip the code and move on to the controller action used to process this form information.

Zend Framework Registration Form
Figure 1. A simple user registration form

Processing the User Registration

With the account table, model, and registration form in place, it's time to write the code which will validate the user's registration information and create the account. For the purposes of this exercise presume the the form action points to an action named register created within a controller named account:

  01 public function registerAction()
  02 {   
  03   // Create array for errors
  04   $this->view->errors = array();
  05 
  06   if ($this->getRequest()->isPost()) {
  07 
  08     $customer = new Default_Model_Customer();
  09 
  10     $result = $customer->save($this->_request->getPost());
  11   
  12     if (! is_array($result)) {
  13    
  14       $this->view->created = 1;                     
  15     
  16     } else {
  17   
  18       $this->view->errors = $result;
  19    
  20       $this->view->firstName     = $this->_request->getPost('first-name');
  21       $this->view->lastName      = $this->_request->getPost('last-name');
  22       $this->view->email         = $this->_request->getPost('email');
  23       $this->view->pswd          = $this->_request->getPost('pswd'); 
  24       $this->view->userName      = $this->_request->getPost('username'); 
  25   
  26     }     
  27 
  28   }
  29   
  30 }

Let's review some of the stickier parts of this action:

  • Line 04 initializes an array which will contain any errors triggered throughout the data validation process.
  • If the registration form has been submitted, line 06 will detect the POSTed data, setting the validation and account creation process into motion.
  • Line 10 attempts to create the user account by calling a method named save(), which resides in the Account model. This method contains all of the validation and insertion logic, allowing us to maintain a thin controller containing remarkably little code. I'll show you what the save() method looks like in a moment. For the moment just keep in mind that if an array is returned from the method call, one or more errors occurred, so we'll assign them to a view variable (line 18) and populate some variables for redisplay in the form. Otherwise we'll create a view variable named $this->view->created which will serve as an indicator to inform the user of a successfully created account.

The account model's save() method looks like this:

  01 public function save(array $data)
  02 {
  03  
  04  // Initialize the errors array
  05  $errors = array();
  06  
  07  // First Name
  08  if (! Zend_Validate::is($data['first-name'], 'NotEmpty')) {
  09      $errors[] = "Please provide your first name.";
  10  }
  11 
  12  // Last Name
  13  if (! Zend_Validate::is($data['last-name'], 'NotEmpty')) {
  14      $errors[] = "Please provide your last name.";
  15  }     
  16 
  17  // Does Email already exist?
  18  if (Zend_Validate::is($data['email'], 'EmailAddress')) {
  19 
  20    $result = $this->findByEmail($data['email']);
  21   
  22    if ($result != false) {
  23      $errors[] = "An account using this e-mail address already exists.";
  24    }
  25  
  26  } else {
  27    $errors[] = "Please provide a valid e-mail address.";
  28  }
  29  
  30  // Password must be at least 6 characters
  31  $valid_pswd = new Zend_Validate_StringLength(6,20);
  32  if (! $valid_pswd->isValid($data['pswd'])) {
  33      $errors[] = "Your password must be 6-20 characters.";
  34  }
  35  
  36  // If no errors, insert the 
  37  if (count($errors) == 0) {
  38   
  39    $data = array (
  40      'first_name'         => $data['first-name'],
  41      'last_name'          => $data['last-name'],
  42      'email'              => $data['email'],
  43      'pswd'               => md5($data['pswd']),         
  44      'recovery_key'       => ''      
  45    );
  46   
  47    return $this->insert($data);
  48 
  49  } else {
  50    return $errors;
  51  }
  52  
  53 }

All of this should look pretty straightforward, so I won't dive into a line-by-line breakdown, other than to say that the findByEmail() is a simple method found in the model which verifies that an account using the provided e-mail address doesn't already exist.

Keep in mind that this is only one of several possible ways to create the registration logic. As a rule though I suggest following the "fat model, thin controller" approach as demonstrated here. Zend Framework project lead Matthew Weier O'Phinney published a great blog post about this very matter here.

Creating the User Login Feature

With the prerequisite steps out of the way, we're ready to bring the Zend_Auth component into the picture. Zend_Auth serves several purposes, including providing a simple-to-use mechanism for verifying a user's provided login credentials (typically an e-mail address and password), and then initiating a session which will allow you to determine whether the user is currently logged into the Web site. Presuming a typical login form prompting the user to provide his e-mail address and password, the following login action will use the Zend_Auth component to process the login:

  01 public function loginAction()
  02 {
  03  
  04  if ($this->getRequest()->isPost()) {
  05   
  06    $email = $this->_request->getPost('email');
  07    $password = $this->_request->getPost('password');
  08   
  09    if (empty($email) || empty($password)) {
  10      $this->view->errors[] = "Please provide your e-mail address and password.";
  11    } else {      
  12    
  13      $db = Zend_Db_Table::getDefaultAdapter();
  14      $authAdapter = new Zend_Auth_Adapter_DbTable($db);
  15    
  16      $authAdapter->setTableName('account');
  17      $authAdapter->setIdentityColumn('email');
  18      $authAdapter->setCredentialColumn('pswd');
  19      $authAdapter->setCredentialTreatment('MD5(?)');
  20    
  21      $authAdapter->setIdentity($email);
  22      $authAdapter->setCredential($password);
  23    
  24      $auth = Zend_Auth::getInstance();
  25      $result = $auth->authenticate($authAdapter);
  26    
  27      // Did the participant successfully login?
  28      if ($result->isValid()) {      
  29 
  30        $this->_redirect('/'); 
  31         
  32      } else {
  33        $this->view->errors[] = "Login failed. Have you confirmed your account?";
  34      }
  35    
  36   }
  37   
  38  }  
  39 } 

Let's review this code:

  • Line 16 identifies the table named used to store the account information. In our case, that table name is account.
  • Line 17 defines the table column which contains the user's "login". We're using an e-mail address, so I've identified that column as email.
  • Line 18 defines the table column which contains the user's password.
  • Line 19 determines how the password will be identified within the table. Because we've used the md5() function to hash the password, the credential treatment is set as you see it here.
  • Lines 21 and 22 assign the provided e-mail address and password to the adapter's identity and credential properties.
  • Line 24 determines whether the user is already logged in, and if not attempts to authenticate him using the authenticate() method.
  • Finally, the isValid() method is used to determine whether the provided credentials were valid. If so, we'll redirect the user to the home page. Otherwise, errors will be output to the login page.

Determining if a User is Logged In

Determining if a user is logged in is easily done using Zend_Auth's getIdentity() method:

$user = Zend_Auth::getInstance()->getIdentity();

You can place this call in a controller's init() method or elsewhere to determine if the user is logged in. If $user is set, you'll be able to retrieve for instance his e-mail address or primary key by referencing the $user object's e-mail or id properties, respectively.

Creating the Logout Mechanism

Finally, to log the user out of the site, just create an action named for instance logout, and point a hyperlink to it:

public function logoutAction()
{
  
 Zend_Auth::getInstance()->clearIdentity();
 $this->_redirect('/');
    
}

Conclusion

Managing user accounts is made incredibly easy using the powerful Zend_Auth component. Hopefully this tutorial provided you with the foundation for giving users access to the custom content they desire!

Resources

About the Author

Jason Gilmore is founder of EasyPHPWebsites.com, and author of the popular book, "Easy PHP Websites with the Zend Framework". Formerly Apress' open source editor, Jason fostered the development of more than 60 books, along the way helping to transform their open source line into one of the industry’s most respected publishing programs. Over the years he's authored several other books, including the best-selling Beginning PHP and MySQL: From Novice to Professional (currently in its third edition), Beginning PHP and PostgreSQL: From Novice to Professional, and Beginning PHP and Oracle: From Novice to Professional.

Jason is a cofounder and speaker chair of CodeMash, a nonprofit organization tasked with hosting an annual namesake developer’s conference, and was a member of the 2008 MySQL Conference speaker selection board.

Jason has published more than 100 tutorials and articles within prominent publications such as Developer.com, Linux Magazine, and TechTarget.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date