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
. The
columnspswd
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.
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 thesave()
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
- The
Zend_Auth
Component: The Zend Framework’s Zend_Auth component documentation - The
Zend_Session
Component: The Zend Framework’s Zend_Session component documentation - PHP’s Session Handling Capabilities: The PHP manual’s session handling documentation
About the Author
Jason Gilmore is founder of EasyPHPWebsites.com
A>, 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.