Easy PHP Websites with the Zend Framework (phần 5) doc - Pdf 16

181CHAPTER 7 • PROCESSING FORMS AND SENDING EMAIL
keep in mind you need to create the directory images/captcha (the images directory residing in your
application's public directory), or alternatively use Zend_Captcha_Image's setImgDir() method to
explicitly specify an alternative directory.
Validating the CAPTCHA
Earlier I mentioned Zend_Captcha will handle the CAPTCHA validation for you. To validate a capt-
cha, just tell Zend_Captcha_Image the name of the form variable which will be passed back to the
controller when instantiating the class. Then when the form variable is passed back, use the
isVal-
id()
method like you would any other validation class:
01 $captcha = new Zend_Captcha_Image(array('name' => 'gamer', 'wordLen' => 5));
02 $captcha->setFont("/usr/share/fonts/truetype/ttf-bitstream-vera/VeraBd.ttf");
03 $captcha->generate();
04 $this->view->captcha = $captcha->render($this->view);
05
06 if ($captcha->isValid($this->_request->getPost('gamer'))) {
07 echo "VALID!";
08 }
Conclusion
The impressive form creation, validation and ltering features offered by the Zend Framework leave
you with no excuse for not properly vetting input arriving from external sources. Be sure to take ad-
vantage of these time-saving (and perhaps job-saving) features no matter the type of website. Like-
wise, the Zend_Mail component makes sending e-mail from your website a total breeze, replacing the
tricky and error-prone strategies of the past.
The next chapter tackles another important topic: user management. In this chapter you'll learn how
implement user registration and login mechanisms, along with requisite features such as password
recovery.
Download at Boykma.Com
Download at Boykma.Com
CHAPTER 8

taining the user's session as he interacts with the site, and logging the user out of the site. In
this step you'll learn how these features are implemented. You'll also learn how to create a
password recovery feature so the user can autonomously reset his password in the event it is
forgotten.
• Step #4: Displaying User Proles: Chances are you'll want to display user prole informa-
tion on the website, in addition to other site content he's compiled over time. In this section
Download at Boykma.Com
184 CHAPTER 8 • MANAGING YOUR USER COMMUNITY
you'll learn how to do this. For the purposes of demonstration this example will only include
the user's name and last login date, but it will nonetheless serve as a basis for adding addi-
tional content. In fact, in the nal step (discussed next) you'll learn how to integrate the user's
friends list into this prole page.
• Step #5. Making Friends: In the nal step of this chapter we'll create a mechanism for giv-
ing users the ability to identify other users as friends, thereby opening up the possibility for
you to build features which allow friends to track the interactions of each other on the site,
such as new additions to their game collection, or status updates regarding what game they're
currently playing.
Step #1. Creating the Users Table and Model
Before creating any of the scripts used to power the aforementioned features, it makes sense to rst
spend some time designing the table and model used to store and manage the user data. Let's start by
creating the
users table, subsequently building the Users model based on the corresponding schema.
The Users Table
The users table will store each user's key account information, namely the username and password,
prole-related information such as his name and gender, and data which will help us gather simple
usage metrics, such as when the account was created, when it was last updated, and when the user last
logged into the system. The
users table follows:
CREATE TABLE users (
id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,

his hands on the table data. Each time the user logs in, we will encrypt the provided password
and compare it to the stored encrypted string. You'll learn how this encryption process occurs
in the section "Registering a New User Account".
• The
registration_key column stores a random 32-character string used as an important
part of the registration conrmation process. You'll learn more about the role of this column
in the section "Conrming Registration".
• The
conrmed column is set once the user has conrmed his e-mail address. You'll learn more
about the role of this column in the section "Conrming Registration".
• The
handle column is used to refer to the gamer on the site and among friends. Think of the
handle as a fun nickname, much like a gamer handle used when playing the Xbox 360.
• The
rst_name and last_name columns are used as alternative ways to refer to the user
when we need to do for more formal reasons, such as when sending administrative emails.
• The
gender column identies the user's gender, and is primarily used for referring to the
proper possessive grammar when talking about for instance the user's game collection (e.g.
Jason has 14 games in his collection), but is also useful for determining website user trends.
• The
created_at, updated_at, and last_login columns are used to determine when the
user's account was created, last updated, and last logged into, respectively.
With the
users table created, it's time to create the data model we'll use to interact with it.
The Users Model
As is typical, the model used to represent the users table is actually broken into two parts: the User
model, which is used for performing actions against the users table writ large, and the UserRow
model, which is used for accessing and manipulating the users table at the row level. In this section
we'll build both models, and subsequently use them in later sections.

22 protected $_rowClass = 'UserRow';
23
24 /**
25 * Retrieve a user using his email address
26 *
27 * @param string $email
28 * @return UserRow
29 */
30 function getUserByEmail($email)
31 {
32 $query = $this->select();
33 $query->where('email = ?', $email);
34 $result = $this->fetchRow($query);
35 return $result;
36 }
37
38 /**
39 * Retrieve user according to his gamer handle
40 *
41 * @param string $handle
42 * @return UserRow
43 */
44 function getUserByHandle($handle)
45 {
46 $query = $this->select();
47 $query->where('handle = ?', $handle);
48 $result = $this->fetchRow($query);
Download at Boykma.Com
187CHAPTER 8 • MANAGING YOUR USER COMMUNITY
49 return $result;

getUserByEmail() method, which can be used to retrieve a user
when all you have available is the user's e-mail address.
• Lines 44-50 dene the
getUserByHandle() method, which can be used to retrieve a user
when all you have available is the user's e-mail address.
• Lines 57-62 dene a useful method we'll use to determine the total number of users in the
system. For instance, GameNomad uses this method to display the total number of registered
users at the top right of each page.
Creating the UserRow Model
Once a user or group of users have been identied using the User model, you can begin perform-
ing row-specic operations using the UserRow model. Listing 8-2 presents a simple example of this
model, complete with a simple method. Later in this chapter we'll add other methods to the model as
Download at Boykma.Com
188 CHAPTER 8 • MANAGING YOUR USER COMMUNITY
other features are introduced.
Listing 8-2. The UserRow model
01 class UserRow extends Zend_Db_Table_Row
02 {
03
04 /**
05 * Determines whether the user is male
06 *
07 * @return boolean
08 */
09 function isMale()
10 {
11 if ($this->gender == "m")
12 {
13 return TRUE;
14 } else {

By this point in the book the form syntax should be pretty easy to gure out, so I'll move on to where
the action's at (no pun intended), namely the
Gamers controller's register action. This action is
rather long, so rather than simply pasting in a listing which spans several pages I'll instead focus on
two select sections and leave it to you to review the entire action located in the code download. Let's
begin the review with Listing 8-2, which contains the code used to validate the form elds.
Listing 8-2. The register action's validation tasks
01 // If the form has been submitted, process it
02 if ($this->getRequest()->isPost()) {
03
04 // Valid email address?
05 if (! Zend_Validate::is($this->_request->getPost('email'),
'EmailAddress')) {
06 $this->view->errors[] = "Invalid e-mail address.";
07 } // end valid email
08
09 // E-mail cannot already exist in database
10 $user = new User();
11 $foundUser = $user->getUserByEmail($this->_request->getPost('email'));
12 if ($foundUser->id != "") {
13 $this->view->errors[] = "E-mail address already in database.";
14 }
15
16 // Handle must be between 2 and 20 characters
17 $validHandle = new Zend_Validate_StringLength(2,20);
Download at Boykma.Com
190 CHAPTER 8 • MANAGING YOUR USER COMMUNITY
18 if (! $validHandle->isValid($this->_request->getPost('handle'))) {
19 $this->view->errors[] = "Handle must be between 2 and 14 characters.";
20 } // end valid handle

51
52 // Valid gender?
53 if (! Zend_Validate::is($this->_request->getPost('gender'), 'NotEmpty')) {
54 $this->view->errors[] = "Please identify your gender.";
55 } // end valid gender
56
57 // If errors exist, prepare the form data for inclusion in the form so
58 // the user doesn't have to repopulate the data
59 if (count($this->view->errors) > 0) {
60
61 $this->view->email = $this->_request->getPost('email');
62 $this->view->handle = $this->_request->getPost('handle');
63 $this->view->rst_name = $this->_request->getPost('rst_name');
64 $this->view->last_name = $this->_request->getPost('last_name');
65 $this->view->gender = $this->_request->getPost('gender');
66
Download at Boykma.Com
191CHAPTER 8 • MANAGING YOUR USER COMMUNITY
67 // No errors, add the user to the database and send the conrmation e-mail
68 } else {

Let's review the code:
• We'll process the form once we've determined it's been posted by examining the return value
of the
$this->getRequest()->isPost() method, as shown in line 02.
• Lines 04-55 perform the series of validations, checking various facets of each form eld val-
ue. Should any of the validations fail, an appropriate message will be appended to the
$this-
>view->errors array. These aren't all standard validation procedures as dened by the Zend
Framework; we also rely upon two methods found in the User model (getUserByHandle()

19
20 // Insert the registration data into the database
21 $user = new User();
22 $user->insert($data);
Download at Boykma.Com
192 CHAPTER 8 • MANAGING YOUR USER COMMUNITY
Let's review the code:
• Line 01 continues where we left off in Listing 8-2 (reproducing line 68), inserting the user's
registration data into the users table and sending the conrmation e-mail. Line 04 creates the
random string (how this is done is covered in the next section) used to identify the user during
the conrmation process.
• Lines 07-18 create an array containing the user data which will subsequently be inserted into
users table. Note how the password is encrypted using PHP's
md5() function, which converts
the provided password into a 32-character string which cannot be reverted back to the origi-
nal value. When logging the user into the system we'll also encrypt the provided password
and compare it to the value stored in the password column. We'll also auto-assign the current
timestamp to the created_at, updated_at, and last_login columns.
• Lines 21-22 complete the insertion process by inserting the array into the
users table.
Once the user's registration data has been saved to the database, it's time to prepare and send the con-
rmation e-mail. This nal step of the process is covered in the next section.
Conrming Registration
To combat spammers and individuals who attempt to potentially manipulate the website by register-
ing multiple accounts, it's a good idea to require the user to conrm registration by verifying his pro-
vided e-mail is valid. You can do this by sending a conrmation e-mail to the user's account following
submission of the form. You already learned how accomplish the majority of this task in Chapter 7,
however there are a few other steps to the task making coverage of this topic a worthwhile endeavor.
The conrmation process is typically carried out by asking the user to click on a link found in the
conrmation e-mail. This link will include a unique key which was generated at the moment of reg-

to 32 characters.
• Line 10 denes the set of allowable characters which can appear in the string
• Lines 15-18 build the random string, using the
mt_rand() function to randomly choose an
integer between 0 and 35, which is then used to retrieve a character located in that offset posi-
tion of the $seeds string.
As you saw earlier in this section, we'll call this method when inserting the newly registered user's
record into the database, adding the random string to the
registration_key column. Following that,
we'll e-mail the user:
01 try {
02 // Create a new mail object
03 $mail = new Zend_Mail();
04
05 $mail->setFrom($this->cong->email->from_admin);
06 $mail->addTo($this->_request->getPost('email'),
07 "{$this->_request->getPost('rst_name')}
08 {$this->_request->getPost('last_name')}");
09 $mail->setSubject('GameNomad: Please conrm your registration');
10
11 include "_email-conrm-registration.phtml";
12
13 $mail->setBodyText($email);
14 $mail->send();
15
16 $this->view->success = 1;
17 } catch (Exception $e) {
18 $this->view->errors[] = "We were unable to send your conrmation
e-mail. Please contact {$this->cong->email->support}.";
19 }

15 email;
16
17 ?>

The code summary follows:
• Because this code is included into the register action, any variables found in the string as-
signed to
$email will be interpolated in the scope of the action. Therefore the variables found
on lines 04, 09, and 11 will all be converted to their appropriate values before being assigned
along with the rest of the string to the
$email variable.
Once the user receives the e-mail, he can click on the conrmation link, which would look very simi-
lar to this:
http://www.gamenomad.com/gamers/verify/key/vhniwd6omzw87txqsdboi5uyf3bol4

As you can surmise by this URL, we'll need to add another action to the
Gamers controller named
verify. This action will accept as input a parameter named key which contains the registration key:
01 /**
02 * Completes the registration process by validating a user's email address
03 *
04 */
05 public function verifyAction() {
06
07 $this->view->pageTitle = "Complete the Registration Process";
Download at Boykma.Com
195CHAPTER 8 • MANAGING YOUR USER COMMUNITY
08
09 // Retrieve the key from the URL
10 $registrationKey = $this->_request->getParam('key');

<?php if (count($this->errors) > 0) { ?>
<div id="errors">
Errors were encountered while creating your account:<br />
<?= $this->Errors($this->errors); ?>
</div>
<?php } ?>
<?php if ($this->success == 1) { ?>
<p>
Congratulations <?= $this->rstName; ?>, your e-mail address has
been veried! <a href="/gamers/login">Login to your account</a>
and begin building your game collection.
Download at Boykma.Com
196 CHAPTER 8 • MANAGING YOUR USER COMMUNITY
</p>
<?php } ?>

Step #3. Managing User Logins
Once the user has conrmed registration, he's free to begin logging into the site and take advantage of
any special features afforded to registered users. In this section you'll learn how to create the interface
and action used to log the user into and out of your website, as well as create a password recovery
tool in the event the user forgets it.
Logging the User into the Website
To login to the website, the user must provide an e-mail address and password, as shown in Figure
8-2. This form points to the Gamers controller's login interface, which will be dissected in this section.
Figure 8-2. The login interface
The
login action is responsible for comparing the provided e-mail address and password with those
found in the users table. While this is easily done using a simple table query, other more complex
issues remain. Notably, if a match is found, we need to establish a session which will keep the user
logged into the site as he navigates from one page to the next.

20 // Identify the column used to store the "username"
21 $authAdapter->setIdentityColumn('email');
22
23 // Identify the column used to store the password
24 $authAdapter->setCredentialColumn('password');
25
26 // How is the password stored?
27 $authAdapter->setCredentialTreatment('MD5(?)');
28
29 // Pass the provided information to the adapter
30 $authAdapter->setIdentity($email);
31 $authAdapter->setCredential($password);
32
33 $auth = Zend_Auth::getInstance();
34 $result = $auth->authenticate($authAdapter);
35
36 // Did the participant successfully login?
37 if ($result->isValid()) {
38
39 // Retrieve the user so can update the login timestamp
40 $user = new User();
41 $updateLogin = $user->getUserByEmail($email);
42
43 if ($updateLogin->conrmed == 1) {
44
45 // Update the login timestamp and save the row
46 $updateLogin->last_login = date('Y-m-d H:i:s');
47 $updateLogin->save();
48
Download at Boykma.Com

• Lines 21 and 24 identify the column names used to store the identity, or username (in our case
the e-mail address), and the password.
• Line 27 denes the encryption scheme used to encode the password. This works like a pre-
pared statement; you could substitute
MD5() for any supported PHP function, although MD5()
is recommended.
• Line 33 invokes the Zend_Auth class in a special way. Instead of using the
new operator, we
use getInstance() to make sure there's only ever one instance of the class available to the
script; if there isn't, getInstance() will create one, otherwise it will use the one already
instantiated. This is known as the Singleton strategy (or pattern).
• Line 34 uses the newly created object to authenticate the user, passing in the adapter informa-
tion. If the e-mail address and password match a pair found in the
users table (line 37), lines
38 through 58 carry out various custom tasks such as updating the user's last_login column
and redirecting the user to the index page.
Download at Boykma.Com
199CHAPTER 8 • MANAGING YOUR USER COMMUNITY
In this last bullet point I mentioned we're redirecting the user to the index page. While I prefer to do
this for reasons of expediency, chances are you'll want to offer some indication to the user that he has
indeed successfully logged in. For instance, Figure 8-3 presents two screenshots indicating how the
GameNomad website depicts a user's login status.
Figure 8-3. Determining the user's login status
You can easily determine the user's login status using the Zend_Auth class'
hasIdentity() and
getIdentity() methods, as is demonstrated below. I place this snippet in the init() method of
controllers where I'd like to ascertain this status, setting a
$user variable intended for access within
the view scope:
if (Zend_Auth::getInstance()->hasIdentity()) {

04 */
05 public function logoutAction()
06 {
07 Zend_Auth::getInstance()->clearIdentity();
08 $this->_redirect('/');
09 }

This action will log the user out of the application by rst deleting the session (line 07) and then
redirecting the user to the website's index page. Any subsequent attempts to access pages requiring
authentication will be denied until the user logs in anew.
Note you won't need a corresponding view for the
logout action, because the user is immediately
redirected to the home page.
Resetting the User's Password
Over time users have devised innumerable strategies in an attempt to recall the often dozens of user
passwords they're required to create and manage in order to perform their daily online activities.
Inevitably though, a user will eventually forget a password, barring his irresponsible use of the same
password with every account. To minimize the user's frustration when the inevitable does occur, your
site should have a password recovery feature which allows the user to easily reset and recover the
new password. Neglecting to include this feature will ultimately result in users contacting you with
requests to reset the password for them, creating a new and therefore redundant account, or worst of
all, quitting coming to your site altogether out of frustration. Fortunately, creating this feature is easy!
VIDEO. Recovering Passwords with Zend_Auth
The Zend_Auth component makes managing user registrations and logs very easy, but the process
of recovering passwords is often a bit more confusing. This video discusses the factors involved
in recovering user passwords, and showing you how to implement this feature using Zend_Auth.
Watch the video at http://www.easyphpwebsites.com/zfw/videos/.
Because for security purposes the user's chosen password has been encrypted using a one-way
algorithm, there is no way to simply retrieve and send it to the user. Therefore we'll need to create a
solution for allowing the user to explicitly reset the password. In order to do so securely, the user will

a breakdown of the relevant code.
Listing 8-6. Resetting the user's password
01 /**
02 * Completes password recovery process
03 */
04
Download at Boykma.Com
202 CHAPTER 8 • MANAGING YOUR USER COMMUNITY
05 public function resetAction()
06 {
07
08 $this->view->pageTitle = "Reset your password";
09
10 // If the form has been posted, reset the password
11 if ($this->getRequest()->isPost()) {
12
13 // Password must be at least 6 characters
14 $valid_pswd = new Zend_Validate_StringLength(6,20);
15 if (! $valid_pswd->isValid($this->_request->getPost('password'))) {
16 $this->view->errors[] = "Password must be at least 6 characters.";
17 } // end valid password
18
19 if ($this->_request->getPost('password') !=
20 $this->_request->getPost('password2')) {
21 $this->view->errors[] = "Your passwords do not match.";
22 }
23
24 // No errors, so update the password
25 if (count($this->view->errors) == 0) {
26

55 $result->save();
56 $this->view->success = 1;
57 $this->view->key = $recoveryKey;
58 } else {
59 $this->view->errors[] = "Unable to locate password recovery key.";
60 }
61
62 }
63 }

Let's review some code:
• If the form has been posted, lines 13-22 will perform two validations, ensuring the provided
password is of a length between six and twenty characters, and also making sure the password
and conrmation password match.
• If validation is successful, lines 27-30 retrieve the user's row by looking up the registration
key. If it's located, lines 32-36 perform the password update, making sure the provided pass-
word is rst hashed using the
md5() function before saving the password to the database.
• Lines 45-63 execute if the user is retrieving the form for the rst time (presumably by way
of clicking on the link found in the password recovery e-mail). Lines 50-52 use the provided
recovery key to determine whether the key exists in the database. If so, the form is presented.
Otherwise, an error message is displayed.
Step #4. Displaying User Proles
Most, if not all social network driven sites provide users with the ability to view at least some part
of the other registered users' proles. Of course, you might limit the display of certain parts of the
proles to just the user's friends (discussed in the next step), but attributes such as each user's rst and
last name, gender, and time of last login seem to be fair game.
You'll also want to provide users with an easy way to point others to their prole. One easy way is by
using an easily recallable URL which includes the user's gaming handle, such as:
http://www.gamenomad.com/gamers/prole/wjgilmore

<div id="errors">
Errors were encountered while searching for this user:<br />
<?php echo $this->Errors($this->errors); ?>
</div><br />
<?php } else { ?>

<p>
The user <b><?= $this->escape($this->gamer->handle); ?></b> has been a member
since <b><?= date('F d, Y', strtotime($this->gamer->created_at)); ?></b>,
and last logged in on
<b><?= date('F d, Y @ h:i:s', strtotime($this->gamer->last_login)); ?></b>.
</p>

<?php } ?>

Step #5. Making Friends
In this fth and nal step of the chapter, we'll discuss one of the fundamental aspects of any website
sporting a social slant: connecting users. By granting users the ability to connect with their friends,
we can start offering an abundant array of new and interesting social features, such as knowing when
your friends are also online and viewing restricted content only available to a user's network. Integrat-
ing the basic framework for implementing these sorts of features is easier than you might think!
To begin, we need a way to map these connections. But for starters, how are the connections initiated
in the rst place? It wouldn't be acceptable to allow users to automatically add friends to their list; in-
stead, one user would have to invite another to join his network. This is done in a manner very similar
to that already used in this chapter to conrm user registration and change a password: by generating
Download at Boykma.Com
205CHAPTER 8 • MANAGING YOUR USER COMMUNITY
a unique key which is attached to an invitation. The user invites a user by initiating a sequence of
events which creates an invitation, generates a unique ID, and mails the invitation to the prospective
friend-in-question. While you're by now familiar enough with this process that I won't go into it anew

Next let's take a look at the
Invitation model.
Listing 8-8. The Invitation model
01 class Invitation extends Zend_Db_Table_Abstract
02 {
03
04 protected $_name = 'invitations';
05 protected $_primary = 'id';
06
07 protected $_referenceMap = array (
08 'Inviter' => array (
09 'columns' => array('inviter_id'),
10 'refTableClass' => 'User'
11 ),
12 'Invitee' => array (
Download at Boykma.Com


Nhờ tải bản gốc

Tài liệu, ebook tham khảo khác

Music ♫

Copyright: Tài liệu đại học © DMCA.com Protection Status