Tải bản đầy đủ (.pdf) (85 trang)

Beginning PHP 5.3 phần 6 pot

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (852.57 KB, 85 trang )

388
Part III: Using PHP in Practice
Creating the DataObject Class File
Now comes the first of the classes that are used in the application. DataObject is an abstract class from
which you can derive classes to handle database access and data retrieval. Because it ’ s an abstract class,
you can ’ t instantiate (create objects from) it directly. In a moment, you create classes to handle both
members and access log records that are based on the
DataObject class.
In OOP parlance, these types of classes are said to follow the active record design pattern, which means
that the object contains the data for the record to store in or retrieve from the database, as well as the
methods to carry out the actual storage and retrieval.
Save the following script as
DataObject.class.php in the book_club folder:
< ?php

require_once “config.php”;

abstract class DataObject {

protected $data = array();

public function __construct( $data ) {
foreach ( $data as $key = > $value ) {
if ( array_key_exists( $key, $this- > data ) ) $this- > data[$key] =
$value;
}
}

public function getValue( $field ) {
if ( array_key_exists( $field, $this- > data ) ) {
return $this- > data[$field];


} else {
die( “Field not found” );
}
}

public function getValueEncoded( $field ) {
return htmlspecialchars( $this- > getValue( $field ) );
}

protected function connect() {
try {
$conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD );
$conn- > setAttribute( PDO::ATTR_PERSISTENT, true );
$conn- > setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
} catch ( PDOException $e ) {
die( “Connection failed: “ . $e- > getMessage() );
}

return $conn;
}

protected function disconnect( $conn ) {
c13.indd 388c13.indd 388 9/21/09 9:12:02 AM9/21/09 9:12:02 AM
Chapter 13: Retrieving Data from MySQL with PHP
389
$conn = “”;
}
}

? >


So how does this class work? First of all, the script includes the config.php file so that it can access the
database configuration constants:

require_once “config.php”;

The PHP require_once() function imports another PHP script into the current script in a similar way to

require() , which you ’ ve used in previous chapters. The difference is that require_once() ensures that
the file is imported only once. This is important if you create a large application with lots of script files,
many of which need to include the same file, such as
config.php . If you used require() , the PHP engine
would include
config.php every time it encountered the require() function call, resulting in multiple
copies of the
config.php file being included in the application (with, needless to say, chaotic results).
Find out more about
require_once() and related functions in Chapter 20.
Next, the class declares a protected
$data array to hold the record ’ s data. The fact that it ’ s protected
means that the classes that derive from this class will be able to use it, but it ’ s still hidden from the
outside world (as most properties should be).
The first method,
__construct() , is the class ’ s constructor. It ’ s called whenever a new object is created
based on a class that ’ s derived from this class. The constructor accepts an associative array of field names
and values (
$data ) and stores them in the protected $data array (assuming each field name exists in

$data ). In this way it ’ s possible for outside code to create fully populated data objects.
The

getValue() method accepts a field name, then looks up that name in the object ’ s $data array. If
found, it returns its value. If the field name wasn ’ t found, the method halts execution with an error
message.
getValue() enables outside code to access the data stored in the object.

getValueEncoded() is a convenience method that allows outside code to retrieve a field value that has
been passed through PHP ’ s
htmlspecialchars() function. This function encodes markup characters
such as < and > as
& lt; and & gt; . Not only is this required when generating XHTML markup, but it ’ s
also a good security measure that can help to reduce the risk of malicious markup making its way into
your Web page.
The final two protected functions allow classes to create a PDO connection to the database, as well as
destroy a database connection.
connect() creates a new PDO object and returns it to the calling code.
Along the way, it sets a couple of useful attributes:

$conn- > setAttribute( PDO::ATTR_PERSISTENT, true );
$conn- > setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

Setting the PDO::ATTR_PERSISTENT attribute to true allows PHP to keep the MySQL connection open
for reuse by other parts of the application (or other applications). With this attribute set to
false , which
is the default setting, a new MySQL connection is opened every time a new PDO object is created in the
application. Because setting up a new MySQL connection takes both time and resources, setting this
attribute to
true can help to improve performance.
c13.indd 389c13.indd 389 9/21/09 9:12:03 AM9/21/09 9:12:03 AM
390
Part III: Using PHP in Practice

Setting the PDO::ATTR_ERRMODE attribute to PDO::ERRMODE_EXCEPTION tells PDO to throw exceptions
whenever a database error occurs, as you saw in the previous chapter.
The
disconnect() function merely takes a PDO object, stored in $conn , and assigns an empty string to

$conn , thereby destroying the object and closing the connection to the MySQL database.
Building the Member Class
The Member class inherits from the DataObject class you just created. It ’ s responsible for retrieving
records from the
members table in the database. The class is relatively straightforward, because a lot of
the work is delegated to the
DataObject class.
Save the following code as
Member.class.php in your book_club folder:
< ?php

require_once “DataObject.class.php”;

class Member extends DataObject {

protected $data = array(
“id” = > “”,
“username” = > “”,
“password” = > “”,
“firstName” = > “”,
“lastName” = > “”,
“joinDate” = > “”,
“gender” = > “”,
“favoriteGenre” = > “”,
“emailAddress” = > “”,

“otherInterests” = > “”
);

private $_genres = array(
“crime” = > “Crime”,
“horror” = > “Horror”,
“thriller” = > “Thriller”,
“romance” = > “Romance”,
“sciFi” = > “Sci-Fi”,
“adventure” = > “Adventure”,
“nonFiction” = > “Non-Fiction”
);

public static function getMembers( $startRow, $numRows, $order ) {
$conn = parent::connect();
$sql = “SELECT SQL_CALC_FOUND_ROWS * FROM “ . TBL_MEMBERS . “ ORDER BY
$order LIMIT :startRow, :numRows”;

try {
$st = $conn- > prepare( $sql );
$st- > bindValue( “:startRow”, $startRow, PDO::PARAM_INT );
$st- > bindValue( “:numRows”, $numRows, PDO::PARAM_INT );
$st- > execute();
c13.indd 390c13.indd 390 9/21/09 9:12:03 AM9/21/09 9:12:03 AM
Chapter 13: Retrieving Data from MySQL with PHP
391
$members = array();
foreach ( $st- > fetchAll() as $row ) {
$members[] = new Member( $row );
}

$st = $conn- > query( “SELECT found_rows() AS totalRows” );
$row = $st- > fetch();
parent::disconnect( $conn );
return array( $members, $row[“totalRows”] );
} catch ( PDOException $e ) {
parent::disconnect( $conn );
die( “Query failed: “ . $e- > getMessage() );
}
}

public static function getMember( $id ) {
$conn = parent::connect();
$sql = “SELECT * FROM “ . TBL_MEMBERS . “ WHERE id = :id”;

try {
$st = $conn- > prepare( $sql );
$st- > bindValue( “:id”, $id, PDO::PARAM_INT );
$st- > execute();
$row = $st- > fetch();
parent::disconnect( $conn );
if ( $row ) return new Member( $row );
} catch ( PDOException $e ) {
parent::disconnect( $conn );
die( “Query failed: “ . $e- > getMessage() );
}
}

public function getGenderString() {
return ( $this- > data[“gender”] == “f” ) ? “Female” : “Male”;
}


public function getFavoriteGenreString() {
return ( $this- > _genres[$this- > data[“favoriteGenre”]] );
}
}

? >

First the script includes the DataObject class file so that it can derive the Member class from

DataObject . Next the class sets up the $data array keys, initializing each value to an empty string. Not
only does this let you see at - a - glance the data that the
Member class works with, but it also enables the

DataObject class ’ s __construct() method to validate each field name that ’ s passed to it when
creating the object. If a field name is passed that isn ’ t in the
$data array, it ’ s rejected.
The class also creates a private array,
$_genres , to map the ENUM values of the favoriteGenre field
in the
members table (for example, “ nonFiction “ ) to human - readable strings (such as “ Non - Fiction ” ).
c13.indd 391c13.indd 391 9/21/09 9:12:04 AM9/21/09 9:12:04 AM
392
Part III: Using PHP in Practice
The next two static methods, getMembers() and getMember() , form the core of the class.

getMembers() expects three arguments: $startRow , $numRows , and $order . It returns a list of

$numRows records from the members table, ordered by $order and starting from $startRow . The
records are returned as an array of

Member objects.
After calling the
DataObject class ’ s connect() method to create a database connection, the method
sets up the SQL statement to retrieve the rows:

$sql = “SELECT SQL_CALC_FOUND_ROWS * FROM “ . TBL_MEMBERS . “ ORDER BY
$order LIMIT :startRow, :numRows”;

Much of this statement will be familiar to you. It ’ s selecting all columns ( * ) from the members table,
ordered by the
$order variable, and limited to the range specified by the $startRow and $numRows
variables. However, there are a couple of concepts here that you haven ’ t seen before.

SQL_CALC_FOUND_ROWS is a special MySQL keyword that computes the total number of rows that
would be returned by the query, assuming the
LIMIT clause wasn ’ t applied. So if the query would return
20 records, but the
LIMIT clause limits the returned rows to five, SQL_CALC_FOUND_ROWS returns a value
of 20. This is useful because it enables you to display the records over several pages, as you see in a
moment.

:startRow and :numRows are called placeholders or parameter markers . They serve two purposes. First of
all, they let you prepare — that is, get MySQL to parse — a query once, then run it multiple times with
different values. If you need to run the same query many times using different input values — when
inserting many rows of data, for instance — prepared statements can really speed up execution.
Secondly, they reduce the risk of so - called SQL injection attacks. For example, an alternative to using
placeholders might be to write:

$sql = “SELECT SQL_CALC_FOUND_ROWS * FROM “ . TBL_MEMBERS . “ ORDER BY
$order LIMIT $startRow, $numRows”;


However, imagine that, due to insufficient checking of user input, a malicious user managed to set

$numRows to “ 1; DELETE FROM members ”. This would run the query as intended, but it would also
run the second statement, which would delete all records from your
members table!
When you use placeholders, you pass data to the query via PDO (as you see shortly), not directly into
your query string. This allows PDO to check the passed data to ensure that it only contains what it ’ s
supposed to contain (integers in this case).
The next block of code is inside a
try catch construct. This ensures that any PDO exceptions that
occur during the query are caught by the method. First, the method calls the
prepare() method of the

PDO object, passing in the SQL string just created:
$st = $conn- > prepare( $sql );

This sets up the query in the MySQL engine, and returns a PDOStatement object to work with (stored in
the
$st variable). Next, the two :startRow and :numRow placeholders you created earlier are populated
with the actual data from the
$startRow and $numRow variables:
$st- > bindValue( “:startRow”, $startRow, PDO::PARAM_INT );
$st- > bindValue( “:numRows”, $numRows, PDO::PARAM_INT );

c13.indd 392c13.indd 392 9/21/09 9:12:04 AM9/21/09 9:12:04 AM
Chapter 13: Retrieving Data from MySQL with PHP
393
The PDOStatement::bindValue() method takes three arguments: the name of the placeholder to
bind, the value to use instead of the placeholder, and the data type of the value (

PDO::PARAM_INT , or
integer, in this case). By specifying the data type, PDO can ensure that the correct type of data is passed
to MySQL. In addition, PDO automatically escapes any quote marks and other special characters in the
data. (Failing to escape special characters is another common cause of SQL injection vulnerabilities.)
Some other common data types that you can use include:

PDO::PARAM_BOOL — A Boolean data type

PDO::PARAM_NULL — The NULL data type

PDO::PARAM_STR — A string data type. (This is the default if you don ’ t specify a type.)

PDO::PARAM_LOB — A LOB data type, such as BLOB or LONGBLOB
Now that the statement has been prepared and the placeholders filled with actual values, it ’ s time to run
the query:

$st- > execute();

The next block of code loops through the record set returned by the query. For each row returned, it
creates a corresponding
Member object to hold the row ’ s data, and stores the object in an array:
$members = array();
foreach ( $st- > fetchAll() as $row ) {
$members[] = new Member( $row );
}

PDOStatement::fetchAll() is one of many ways that you can retrieve the result set returned from a
query.
fetchAll() grabs the whole result set in one go, and returns it as an array of associative arrays,
where each associative array holds a row of data. Though this is fine for relatively small result sets —

say, less than 100 records — be careful of using
fetchAll() with large result sets, because the entire
result set is loaded into your script ’ s memory in one go.
However, in this case
fetchAll() is ideal. The script loops through the returned array of rows, passing
each
$row associative array into the constructor for the Member class. Remember that the constructor is
actually in the
DataObject class, and it expects an associative array of field names and values, which
is exactly what each element of the array returned by
fetchAll() contains. The constructor then uses
this associative array to populate the
Member object with the data.
Once the array of
Member objects has been created, the method runs another query. Remember the SQL_
CALC_FOUND_ROWS
keyword in the original query? To extract the calculated total number of rows, you
need to run a second query immediately after the original query:

$st = $conn- > query( “SELECT found_rows() AS totalRows” );
$row = $st- > fetch();

The query calls the MySQL found_rows() function to get the calculated total, and returns the result as
an alias,
totalRows . Notice that this is a regular query that uses PDO::query() , rather than a prepared
statement as used by the first query. You don ’ t need to use placeholders because the query doesn ’ t need
to contain any passed - in values; hence there is no need to go to the trouble of creating a prepared
statement.





c13.indd 393c13.indd 393 9/21/09 9:12:04 AM9/21/09 9:12:04 AM
394
Part III: Using PHP in Practice
Finally, the method closes the database connection, then returns the data to the calling code in the form
of a two - element array. The first element contains the array of
Member objects, and the second element
contains the calculated total number of rows:

return array( $members, $row[“totalRows”] );

Of course, after the try block comes the corresponding catch block. This simply closes the connection
and uses PHP ’ s
die() function to abort the script with an error message.
The next method,
getMember() , works in a similar fashion to getMembers() . It retrieves a single record
from the
members table, as a Member object. The ID of the record to retrieve is specified by the argument
passed to the method.
This method creates a prepared statement, much like
getMembers() did, to retrieve the record:
$sql = “SELECT * FROM “ . TBL_MEMBERS . “ WHERE id = :id”;

Next, the $id parameter ’ s value is bound to the :id placeholder, and the query is run:
$st- > bindValue( “:id”, $id, PDO::PARAM_INT );
$st- > execute();

If the query returned a row, it is retrieved using the PDOStatement::fetch() method, which retrieves
a single row from the result set as an associative array of field names and values. This associative array is

then passed to the
Member constructor to create and populate a Member object, which is then returned to
the calling code after closing the connection:

$row = $st- > fetch();
parent::disconnect( $conn );
if ( $row ) return new Member( $row );

The final two convenience methods are used to return the Member object ’ s gender and favoriteGenre
fields as human - friendly strings, ideal for displaying in a Web page.
getGenderString() simply
returns “ Female ” if gender is set to
“ f ” , and “ Male ” otherwise. getFavoriteGenreString() looks up
the field value in the
$_genres array property created at the start of the class in order to return a
human - readable form of the value.
Building the LogEntry Class
The LogEntry class is another data class, much like Member , although it ’ s a fair bit simpler. It retrieves
rows of data from the
accessLog table.
Save the following script as
LogEntry.class.php in the book_club folder:
< ?php

require_once “DataObject.class.php”;

class LogEntry extends DataObject {

protected $data = array(
c13.indd 394c13.indd 394 9/21/09 9:12:05 AM9/21/09 9:12:05 AM

Chapter 13: Retrieving Data from MySQL with PHP
395
“memberId” = > “”,
“pageUrl” = > “”,
“numVisits” = > “”,
“lastAccess” = > “”
);

public static function getLogEntries( $memberId ) {
$conn = parent::connect();
$sql = “SELECT * FROM “ . TBL_ACCESS_LOG . “ WHERE memberId = :memberId
ORDER BY lastAccess DESC”;

try {
$st = $conn- > prepare( $sql );
$st- > bindValue( “:memberId”, $memberId, PDO::PARAM_INT );
$st- > execute();
$logEntries = array();
foreach ( $st- > fetchAll() as $row ) {
$logEntries[] = new LogEntry( $row );
}
parent::disconnect( $conn );
return $logEntries;
} catch ( PDOException $e ) {
parent::disconnect( $conn );
die( “Query failed: “ . $e- > getMessage() );
}
}
}


? >

As with Member , the LogEntry class derives from the DataObject abstract class. Its protected $data
array contains the field names from the
accessLog table: memberId , pageUrl , numVisits , and

lastAccess .

LogEntry contains just one method, getLogEntries() , that retrieves a list of all accessLog records for
a particular member (specified by
$memberId ) as LogEntry objects. The query sorts the entries in
descending order of access date — that is, newest first:

$sql = “SELECT * FROM “ . TBL_ACCESS_LOG . “ WHERE memberId = :memberId
ORDER BY lastAccess DESC”;

The rest of the method is similar to Member::getMembers() . The statement is prepared, the $memberId
parameter is bound to the
:memberId placeholder, and the query is run. The record set is retrieved as an
array of associative arrays using
PDOStatement::fetchAll() , and each associative array is used to
create a new
LogEntry object, which is then added to an array. The method then returns the array of

LogEntry objects to the calling code.
Creating the view_members.php Script
Now you ’ ve laid all the foundations for your member viewer application; in fact you ’ ve already done
most of the hard work. Now it ’ s just a case of writing two scripts: one to display the list of members, and
another to display details of an individual member.
c13.indd 395c13.indd 395 9/21/09 9:12:05 AM9/21/09 9:12:05 AM

396
Part III: Using PHP in Practice
First, the member list. Save the following code as view_members.php in your book_club folder:
< ?php

require_once( “common.inc.php” );
require_once( “config.php” );
require_once( “Member.class.php” );

$start = isset( $_GET[“start”] ) ? (int)$_GET[“start”] : 0;
$order = isset( $_GET[“order”] ) ? preg_replace( “/[^a-zA-Z]/”, “”,
$_GET[“order”] ) : “username”;
list( $members, $totalRows ) = Member::getMembers( $start, PAGE_SIZE,
$order );
displayPageHeader( “View book club members” );

? >
< h2 > Displaying members < ?php echo $start + 1 ? > - < ?php echo min( $start +
PAGE_SIZE, $totalRows ) ? > of < ?php echo $totalRows ? > < /h2 >

< table cellspacing=”0” style=”width: 30em; border: 1px solid #666;” >
< tr >
< th > < ?php if ( $order != “username” ) { ? > < a href=”view_members.php?
order=username” > < ?php } ? > Username < ?php if ( $order != “username” )
{ ? > < /a > < ?php } ? > < /th >
< th > < ?php if ( $order != “firstName” ) { ? > < a href=”view_members.php?
order=firstName” > < ?php } ? > First name < ?php if ( $order != “firstName” )
{ ? > < /a > < ?php } ? > < /th >
< th >
< ?php if ( $order != “lastName” ) { ? > < a href=”view_members.php?

order=lastName” > < ?php } ? > Last name < ?php if ( $order != “lastName” )
{ ? > < /a > < ?php } ? > < /th >
< /tr >
< ?php
$rowCount = 0;


foreach ( $members as $member ) {
$rowCount++;
? >
<
tr < ?php if ( $rowCount % 2 == 0 ) echo ‘ class=”alt”’ ? > >
< td > < a href=”view_member.php?memberId= < ?php echo $member- >
getValueEncoded( “id” ) ? > ” > < ?php echo $member- > getValueEncoded( “username” )
? > < /a > < /td >
< td > < ?php echo $member- > getValueEncoded( “firstName” ) ? > < /td >
< td > < ?php echo $member- > getValueEncoded( “lastName” ) ? > < /td >
< /tr >
< ?php
}
? >

< /table >


< div style=”width: 30em; margin-top: 20px; text-align: center;”
>
< ?php if ( $start > 0 ) { ? >
< a href=”view_members.php?start= < ?php echo max( $start - PAGE_SIZE, 0 )
? > & amp;order= < ?php echo $order ? > ” > Previous page < /a >

< ?php } ? >
c13.indd 396c13.indd 396 9/21/09 9:12:05 AM9/21/09 9:12:05 AM
Chapter 13: Retrieving Data from MySQL with PHP
397
& nbsp;
< ?php if ( $start + PAGE_SIZE < $totalRows ) { ? >
< a href=”view_members.php?start= < ?php echo min( $start + PAGE_SIZE,
$totalRows ) ? > & amp;order= < ?php echo $order ? > ” > Next page < /a >
< ?php } ? >
< /div >

< ?php
displayPageFooter();
? >

This script makes use of the Member class to retrieve the list of members from the database, then displays
the list in the page. It starts by retrieving two query string parameters —
start , representing the record
position from which to start displaying the records in the page, and
order , which specifies which
column to sort the data by — and storing the parameter values in two variables,
$start and $order . If
either parameter wasn ’ t passed to the script, a default value is used. To improve security, the script filters
both parameters to make sure they contain valid values:
$start is cast to int , whereas $order uses a
regular expression to remove any non - alphabetic characters (because only letters are used for the field
names in the
members table).
You can find out more about regular expressions in Chapter 18.
Next, the script retrieves the list of members to display in the page. The code to do this is really simple,

because all the complexity is nicely hidden away in the
Member class:
list( $members, $totalRows ) = Member::getMembers( $start, PAGE_SIZE, $order );

Member::getMembers() is called, passing in the row to start retrieving records from, the number of
records to retrieve, and the string to use for the
ORDER BY clause. getMembers() dutifully returns a
two - element array. The first element — the list of
Member objects — is stored in $members , and the
second element — the total number of members in the
members table — is stored in $totalRows .
Now that the data is retrieved, it ’ s simply a case of displaying the list in the page. First,

displayPageHeader() is called with an appropriate page title to display the XHTML header. Then an
XHTML
table element is started, with three header cells for username, first name, and last name:
< th > < ?php if ( $order != “username” ) { ? > < a
href=”view_members.php?order=username” > < ?php } ? > Username < ?php if
( $order != “username” ) { ? > < /a > < ?php } ? > < /th >
< th > < ?php if ( $order != “firstName” ) { ? > < a href=”view_members.php?
order=firstName” > < ?php } ? > First name < ?php if ( $order != “firstName” )
{ ? > < /a > < ?php } ? > < /th >
< th > < ?php if ( $order != “lastName” ) { ? > < a href=”view_members.php?
order=lastName” > < ?php } ? > Last name < ?php if ( $order != “lastName” )
{ ? > < /a > < ?php } ? > < /th >

In each case, the column name is linked to a URL that, when visited, runs view_members.php again
with a new
order query parameter to sort the data by that column. However, if the data is already
sorted by a particular column, that column ’ s name isn ’ t linked, in order to indicate that the data is sorted

by that column.
c13.indd 397c13.indd 397 9/21/09 9:12:06 AM9/21/09 9:12:06 AM
398
Part III: Using PHP in Practice
Next, the data is output, one record per table row:
< ?php
$rowCount = 0;

foreach ( $members as $member ) {
$rowCount++;
? >
< tr < ?php if ( $rowCount % 2 == 0 ) echo ‘ class=”alt”’ ? > >
< td > < a href=”view_member.php?memberId= < ?php echo $member- >
getValueEncoded( “id” )
? > ” > < ?php echo $member- > getValueEncoded( “username” ) ? > < /a > < /td >
< td > < ?php echo $member- > getValueEncoded( “firstName” ) ? > < /td >
< td > < ?php echo $member- > getValueEncoded( “lastName” ) ? > < /td >
< /tr >
< ?php
}
? >

For each row, the script displays the values of three fields — username , firstName , and lastName —
for the current member in individual table cells. For each cell, the
Member object ’ s getValueEncoded()
method is called to retrieve the appropriate field value with any special XHTML characters encoded. In
addition, the values in the
username cells are linked to the view_member.php script (which you create
in a moment), passing in the ID of the member whose details should be displayed.


$rowCount is used to track the current row number. If the number is even, the table row ’ s CSS class is
set to
alt , producing an alternating row effect as defined in the CSS in the page header.
The last section of the script produces the links to jump to the previous and next page of members:

< div style=”width: 30em; margin-top: 20px; text-align: center;” >
< ?php if ( $start > 0 ) { ? >
< a href=”view_members.php?start= < ?php echo max( $start - PAGE_SIZE, 0 )
? > & amp;order= < ?php echo $order ? > ” > Previous page < /a >
< ?php } ? >
& nbsp;
< ?php if ( $start + PAGE_SIZE < $totalRows ) { ? >
< a href=”view_members.php?start= < ?php echo min( $start + PAGE_SIZE,
$totalRows ) ? > & amp;order= < ?php echo $order ? > ” > Next page < /a >
< ?php } ? >
< /div >

If the current page doesn ’ t begin at the start of the member list ( $start > 0 ), the “Previous page” link
is created. This links to the same
view_members.php script, passing in a new start value one page less
than the current value. (If the new
start value should happen to be negative, it is set to zero.)
Similarly, if the current page isn ’ t the last page of the member list (
$start + PAGE_SIZE <
$totalRows
), the “Next page” link is created, setting start to one page greater than the current start
value (or
$totalRows if start would end up being greater than $totalRows ).
Notice that both links also pass through the
order query string parameter, ensuring that the correct sort

order is preserved across pages.
c13.indd 398c13.indd 398 9/21/09 9:12:06 AM9/21/09 9:12:06 AM
Chapter 13: Retrieving Data from MySQL with PHP
399
Creating the view_member.php Script
The very last PHP file you need to create is the script to view an individual member ’ s details. Save the
following code as
view_member.php in your book_club folder:
< ?php

require_once( “common.inc.php” );
require_once( “config.php” );
require_once( “Member.class.php” );
require_once( “LogEntry.class.php” );

$memberId = isset( $_GET[“memberId”] ) ? (int)$_GET[“memberId”] : 0;

if ( !$member = Member::getMember( $memberId ) ) {
displayPageHeader( “Error” );
echo “ < div > Member not found. < /div > ”;
displayPageFooter();
exit;
}

$logEntries = LogEntry::getLogEntries( $memberId );
displayPageHeader( “View member: “ . $member- > getValueEncoded( “firstName” )
. “ “ . $member- > getValueEncoded( “lastName” ) );

? >
< dl style=”width: 30em;” >

< dt > Username < /dt >
< dd > < ?php echo $member- > getValueEncoded( “username” ) ? > < /dd >
< dt > First name < /dt >
< dd > < ?php echo $member- > getValueEncoded( “firstName” ) ? > < /dd >
< dt > Last name < /dt >
< dd > < ?php echo $member- > getValueEncoded( “lastName” ) ? > < /dd >
< dt > Joined on < /dt >
< dd > < ?php echo $member- > getValueEncoded( “joinDate” ) ? > < /dd >
< dt > Gender < /dt >
< dd > < ?php echo $member- > getGenderString() ? > < /dd >
< dt > Favorite genre < /dt >
< dd > < ?php echo $member- > getFavoriteGenreString() ? > < /dd >
< dt > Email address < /dt >
< dd > < ?php echo $member- > getValueEncoded( “emailAddress” ) ? > < /dd >
< dt > Other interests < /dt >
< dd > < ?php echo $member- > getValueEncoded( “otherInterests” ) ? > < /dd >
< /dl >


< h2
> Access log < /h2 >


< table cellspacing=”0” style=”width: 30em; border: 1px solid #666;”
>
< tr >
< th > Web page < /th >
< th > Number of visits < /th >
< th > Last visit < /th >
< /tr >

< ?php
c13.indd 399c13.indd 399 9/21/09 9:12:07 AM9/21/09 9:12:07 AM
400
Part III: Using PHP in Practice
$rowCount = 0;

foreach ( $logEntries as $logEntry ) {
$rowCount++;
? >
< tr < ?php if ( $rowCount % 2 == 0 ) echo ‘ class=”alt”’ ? > >
< td > < ?php echo $logEntry- > getValueEncoded( “pageUrl” ) ? > < /td >
< td > < ?php echo $logEntry- > getValueEncoded( “numVisits” ) ? > < /td >
< td > < ?php echo $logEntry- > getValueEncoded( “lastAccess” ) ? > < /td >
< /tr >
< ?php
}
? >
< /table >

< div style=”width: 30em; margin-top: 20px; text-align: center;” >
< a href=”javascript:history.go(-1)” > Back < /a >
< /div >

< ?php
displayPageFooter();
? >

This script expects to be passed a memberId query string parameter specifying the member to be
displayed. This value is then passed to
Member::getMember() to retrieve the record as a Member object.

If nothing is returned from the call to
getMember() , the member could not be found in the members
table, so an error message is displayed and the script exits.
Assuming the member was found and retrieved, the script then calls
LogEntry::getLogEntries() ,
again passing in the member ID, in order to retrieve the rows in the
accessLog table associated with
this member (if any).
Next, the script displays all of the member fields inside an HTML definition list (
dl ) element. Mostly this
is simply a case of calling
Member::getValueEncoded() for each field, passing in the name of the field
to retrieve, and displaying the returned value. For the special cases of gender and favorite genre, the

getGenderString() and getFavoriteGenreString() methods are called to display the field values
in a more human - friendly format.
After the member details come the access log entries. These are displayed in a similar way to the
members in the
view_members.php script. For each log entry, the page URL, number of visits, and last
access date are displayed in a table row. Finally, at the end of the page, a JavaScript link is created to
allow the user to go back to the member list page.
Testing the Application
Now that you ’ ve created all the scripts for the application, it ’ s time to try it out. Open the view_
members.php
script ’ s URL in your Web browser. You should see a page similar to Figure 13 - 1. Try
moving through the pages (there should be two) using the “Next page” and “Previous page” links, and
changing the sort order by clicking the column headings.
c13.indd 400c13.indd 400 9/21/09 9:12:07 AM9/21/09 9:12:07 AM
Chapter 13: Retrieving Data from MySQL with PHP
401

Now click a username in the member list. You ’ ll be taken to the view_member.php script, which should
look similar to Figure 13 - 2. Click the Back link to return to the members list.
Figure 13-2
You ’ re now well on your way to writing complex, database - driven PHP applications. The next logical
step, of course, is to create applications that can write data to a database, rather than just retrieve data,
and you do this in the next chapter.
Summary
In this chapter you expanded on your knowledge of both MySQL and PDO, and learned how to create
PHP applications that are capable of reading data from database tables and displaying the data to the user:
First you set up the tables for an imaginary book club database that you used throughout the
chapter. Along the way, you explored the
BINARY attribute and case - sensitivity; the UNIQUE
constraint for enforcing unique values in a column; the
ENUM data type for creating fields with a
small number of possible values; and the
TIMESTAMP data type for automatically recording
when records are created or updated

c13.indd 401c13.indd 401 9/21/09 9:12:07 AM9/21/09 9:12:07 AM
402
Part III: Using PHP in Practice
Next, you took a closer look at the SQL SELECT statement. You learned how to use LIMIT to
restrict the number of rows returned from a query, and how to sort the rows of a result set using
the
ORDER BY clause. You saw how to make queries more flexible by using the LIKE and NOT
LIKE
operators, and how to use functions such as count() , sum() , min() , max() , and avg() to
summarize columns in a table
Duplicate rows can be a problem in result sets, and you saw how to solve this issue by using the


DISTINCT keyword. You also learned how to group results by a specified column or columns
through the use of
GROUP BY clauses
One of the main advantages of a relational database is that you can pull data from more than
one table at a time — a process known as joining tables. You learned how to do this, and also
how to use aliases to make both queries and result sets more readable
To round off the discussion on
SELECT queries, you explored a few of the myriad MySQL
operators and functions that you can use to add even more power to your queries
In the second half of the chapter you built a member viewer application that was capable of listing all
the members in the fictional book club database, as well as viewing detailed information about each
member. In the process you worked with abstract classes, saw how to create classes to deal with database
table access, learned some more useful features of PDO such as prepared queries, and discovered how to
use MySQL ’ s
SQL_CALC_FOUND_ROWS keyword to help you display table contents over several pages.
You now have a solid grounding in how to construct queries and communicate with MySQL from your
PHP scripts. The next chapter takes things further and looks at how to manipulate data in a database
from within PHP.
Meanwhile, try the following two exercises, which test both your SQL query skills and your PHP
programming skills. You can find the solutions to these exercises in Appendix A.
Exercises
1. Write an SQL query to calculate the total number of page views made by all male visitors to the
book club Web site, as well as the total page views from all female visitors.
2. Referring back to the member viewer application you created in this chapter, modify the Member
class ’ s
getMembers() method to allow an optional fourth parameter, $interest . When this
parameter is specified, the method should only return members whose
otherInterests fields
contain the string supplied in
$interest .





c13.indd 402c13.indd 402 9/21/09 9:12:08 AM9/21/09 9:12:08 AM
14
Manipulating MySQL Data
with PHP
This is the third and final chapter on the topic of building database - driven PHP applications. In
Chapter 12 you came to grips with the basics of MySQL and relational databases, and learned how
to use PDO (PHP Data Objects) to connect to MySQL databases from PHP. Chapter 13 explored the
concept of retrieving data from a database within your PHP scripts; you learned in detail how to
create
SELECT queries, and you wrote a simple record viewer for displaying details of members in
a book club database.
In this chapter, you look at how to alter the data in a MySQL database using PHP. This involves:
Inserting new records into tables using
INSERT statements
Changing field values within records with
UPDATE statements
Deleting records using
DELETE statements
You explore these three operations in detail, and learn how to perform them from within your PHP
scripts.
Once you understand how to manipulate data in a MySQL database, you build an application to
allow new members to register for your book club database and log in to a members - only area of
your Web site, and write some PHP code that you can use to log each member ’ s page views in the
members ’ area. Finally, you extend the member record viewer you created in Chapter 13 to allow
you to edit and delete member records.
Inserting Records

You learned how to use SQL to add records to a table in Chapters 12 and 13. Remember that you
can insert a row of data with:

INSERT INTO
table
VALUES (
value1
,
value2
, );




c14.indd 403c14.indd 403 9/21/09 9:14:02 AM9/21/09 9:14:02 AM
404
Part III: Using PHP in Practice
If you want to insert only some values, leaving NULL s or other default values in the remaining fields, use:
INSERT INTO
table
(
field1
,
field2
, ) VALUES (
value1
,
value2
, );


Though the first approach is compact, and perfectly valid if you want to populate all the fields in the
row, the second approach is generally more flexible and readable.
So how do you insert records using your PHP script? You pass
INSERT statements to MySQL via PDO in
much the same way as you pass
SELECT statements. If you don ’ t want to pass data from any PHP
variables, you can use the simpler
PDO::query() method — for example:
< ?php
$dsn = “mysql:dbname=mydatabase”;
$username = “root”;
$password = “mypass”;

try {
$conn = new PDO( $dsn, $username, $password );
$conn- > setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
} catch ( PDOException $e ) {
echo “Connection failed: “ . $e- > getMessage();
}

$sql = “INSERT INTO members VALUES ( 8, ‘derek’, password(‘mypass’), ‘Derek’,
‘Winter’, ‘2008-06-25’, ‘m’, ‘crime’, ‘’, ‘Watching TV,
motor racing’ )”;

try {
$conn- > query( $sql );
} catch ( PDOException $e ) {
echo “Query failed: “ . $e- > getMessage();
}


? >

Notice that, although the call to $conn - > query() still returns a PDOStatement object, the object is
discarded in this case. There ’ s no result set to examine, so there ’ s no need to hold onto the

PDOStatement object.
However, chances are that you do want to insert data that is stored in PHP variables. For example, if a
member has just registered using a registration form, you ’ ll want to pass the form data to the
INSERT
statement to add the member record. The safest way to do this is to create a prepared statement using

PDO::prepare() , as you did with SELECT queries in the previous chapter. You can then use
placeholders in the query string for each of the field values that you want to insert, and pass the data
into the query using calls to
PDOStatement::bindValue() . For example:
< ?php
$dsn = “mysql:dbname=mydatabase”;
$username = “root”;
$password = “mypass”;

c14.indd 404c14.indd 404 9/21/09 9:14:03 AM9/21/09 9:14:03 AM
Chapter 14: Manipulating MySQL Data with PHP
405
try {
$conn = new PDO( $dsn, $username, $password );
$conn- > setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
} catch ( PDOException $e ) {
echo “Connection failed: “ . $e- > getMessage();
}


$id = 8;
$username = “derek”;
$password = “mypass”;
$firstName = “Derek”;
$lastName = “Winter”;
$joinDate = “2008-06-25”;
$gender = “m”;
$favoriteGenre = “crime”;
$emailAddress = “”;
$otherInterests = “Watching TV, motor racing”;

$sql = “INSERT INTO members VALUES ( :id, :username, password(:password),
:firstName, :lastName, :joinDate, :gender, :favoriteGenre, :emailAddress,
:otherInterests )”;

try {
$st = $conn- > prepare( $sql );
$st- > bindValue( “:id”, $id, PDO::PARAM_INT );
$st- > bindValue( “:username”, $username, PDO::PARAM_STR );
$st- > bindValue( “:password”, $password, PDO::PARAM_STR );
$st- > bindValue( “:firstName”, $firstName, PDO::PARAM_STR );
$st- > bindValue( “:lastName”, $lastName, PDO::PARAM_STR );
$st- > bindValue( “:joinDate”, $joinDate, PDO::PARAM_STR );
$st- > bindValue( “:gender”, $gender, PDO::PARAM_STR );
$st- > bindValue( “:favoriteGenre”, $favoriteGenre, PDO::PARAM_STR );
$st- > bindValue( “:emailAddress”, $emailAddress, PDO::PARAM_STR );
$st- > bindValue( “:otherInterests”, $otherInterests, PDO::PARAM_STR );
$st- > execute();
} catch ( PDOException $e ) {
echo “Query failed: “ . $e- > getMessage();

}

? >

In this example, the variable values are hard - coded in the script. In a real - world application, you would
of course receive these values from outside the script, such as via submitted form values in the
$_POST
superglobal array.
Remember that, although using prepared statements and placeholders gives you some protection against
SQL injection attacks, you should always check or filter user input before doing anything with it, such as
storing it in a database. You can find out more about this and other security - related issues in Chapter 20.
c14.indd 405c14.indd 405 9/21/09 9:14:03 AM9/21/09 9:14:03 AM
406
Part III: Using PHP in Practice
Updating Records
As you saw in Chapter 12, you can alter the data within an existing table row by using an SQL UPDATE
statement:

mysql > UPDATE fruit SET name = ‘grapefruit’, color = ‘yellow’ WHERE id = 2;

Query OK, 1 row affected (0.29 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql > SELECT * from fruit;
+ + + +
| id | name | color |
+ + + +
| 1 | banana | yellow |
| 2 | grapefruit | yellow |
| 3 | plum | purple |

+ + + +
3 rows in set (0.00 sec)

As with inserting new records, updating records via your PHP script is simply a case of using
PDO::query() if you ’ re passing literal values in the UPDATE statement, or PDO::prepare() with
placeholders if you ’ re passing variable values. For example, the following script changes the email
address field in the “ Derek Winter ” record that was added in the previous section:

< ?php
$dsn = “mysql:dbname=mydatabase”;
$username = “root”;
$password = “mypass”;

try {
$conn = new PDO( $dsn, $username, $password );
$conn- > setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
} catch ( PDOException $e ) {
echo “Connection failed: “ . $e- > getMessage();
}

$id = 8;
$newEmailAddress = “”;

$sql = “UPDATE members SET emailAddress = :emailAddress WHERE id = :id”;

try {
$st = $conn- > prepare( $sql );
$st- > bindValue( “:id”, $id, PDO::PARAM_INT );
$st- > bindValue( “:emailAddress”, $newEmailAddress, PDO::PARAM_STR );
$st- > execute();

} catch ( PDOException $e ) {
echo “Query failed: “ . $e- > getMessage();
}

? >

c14.indd 406c14.indd 406 9/21/09 9:14:03 AM9/21/09 9:14:03 AM
Chapter 14: Manipulating MySQL Data with PHP
407
Deleting Records
Deleting rows of data via PHP is a similar process to updating. Chapter 12 showed you how to delete
rows from a table using the SQL
DELETE keyword:
mysql > DELETE FROM fruit WHERE id = 2;
Query OK, 1 row affected (0.02 sec)

To delete rows using PHP, you pass a DELETE statement directly via PDO::query() , or create the statement
using
PDO::prepare() with placeholders, passing in values (such as the criteria for the WHERE clause)
with
PDOStatement::bindValue() and running the query with PDOStatement::execute() .
The following script deletes the member record with the ID of 8 from the
members table:
< ?php
$dsn = “mysql:dbname=mydatabase”;
$username = “root”;
$password = “mypass”;

try {
$conn = new PDO( $dsn, $username, $password );

$conn- > setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
} catch ( PDOException $e ) {
echo “Connection failed: “ . $e- > getMessage();
}

$id = 8;

$sql = “DELETE FROM members WHERE id = :id”;

try {
$st = $conn- > prepare( $sql );
$st- > bindValue( “:id”, $id, PDO::PARAM_INT );
$st- > execute();
} catch ( PDOException $e ) {
echo “Query failed: “ . $e- > getMessage();
}

? >

Incidentally, rather than binding the value of a variable to a placeholder with PDOStatement::
bindValue()
, you can instead use PDOStatement::bindParam() to bind the variable itself. If
you then change the value of the variable after the call to
bindParam() , the placeholder value is
automatically updated to the new value (in other words, the variable is bound by reference rather than
by value). This can be useful if you ’ re not sure what value you ’ re going to pass in at the time you
prepare the statement. Find out more on
bindParam() in the online PHP manual at http://www
.php.net/manual/en/pdostatement.bindparam.php
.

c14.indd 407c14.indd 407 9/21/09 9:14:04 AM9/21/09 9:14:04 AM
408
Part III: Using PHP in Practice
Building a Member Registration Application
Now that you know how to insert records into a MySQL table via PHP, you can write a script that
lets new members sign up for your book club. Rather than reinventing the wheel, you build on the
object - oriented member viewer application that you created in Chapter 13, extending the classes to add
new functionality and creating a script to register new members.
Adding More Common Code
First, add some extra code to the common.inc.php file that ’ s inside your book_club folder. Open this
file in your editor.
Within this file, it makes sense to include the other common files that are used by the rest of the
application. That way, scripts only need to include
common.inc.php , and the other files will be included
automatically. Add the following to the start of the
common.inc.php file:
require_once( “config.php” );
require_once( “Member.class.php” );
require_once( “LogEntry.class.php” );

Now add the following line to the CSS declarations within the displayPageHeader() function:
< style type=”text/css” >
th { text-align: left; background-color: #bbb; }
th, td { padding: 0.4em; }
tr.alt td { background: #ddd; }
.error { background: #d33; color: white; padding: 0.2em; }
< /style >

This line creates a CSS .error class that you ’ ll use to highlight any problems with the registration form.
Finally, add three extra utility functions to help with displaying the registration form:


function validateField( $fieldName, $missingFields ) {
if ( in_array( $fieldName, $missingFields ) ) {
echo ‘ class=”error”’;
}
}

function setChecked( DataObject $obj, $fieldName, $fieldValue ) {
if ( $obj- > getValue( $fieldName ) == $fieldValue ) {
echo ‘ checked=”checked”’;
}
}

function setSelected( DataObject $obj, $fieldName, $fieldValue ) {
if ( $obj- > getValue( $fieldName ) == $fieldValue ) {
echo ‘ selected=”selected”’;
}
}

c14.indd 408c14.indd 408 9/21/09 9:14:04 AM9/21/09 9:14:04 AM
Chapter 14: Manipulating MySQL Data with PHP
409
You may recognize these functions from Chapter 9 (although they ’ ve been slightly modified).

validateField() checks to see if the supplied field name is within the list of fields that the user
forgot to fill in. If it is, a
class= “ error ” attribute is output, which highlights the missing field
in red.
Meanwhile,
setChecked() and setSelected() output the markup to pre - check a checkbox and

pre - select an option in a menu, respectively. Both methods expect an object derived from the

DataObject class (such as a Member object), the name of the field to look up, and the value to
compare. If the supplied value matches the value of the field in the object, the markup is output.
These functions are used within the registration form to help prefill fields in the form, as you see in a
moment.
Enhancing the Member Class
The next thing to do is add some more functionality to your Member class. First, add a couple of extra
methods for retrieving
Member objects from the database. getByUsername() retrieves the member with
the supplied username, and
getByEmailAddress() retrieves the member with the given email address.
These will be used to ensure that a prospective member doesn ’ t accidentally register with a username or
email address that is already in the database.
Open up the
Member.class.php file that you created in Chapter 13 and add the following code to the
file, after the existing
getMember() method:
public static function getByUsername( $username ) {
$conn = parent::connect();
$sql = “SELECT * FROM “ . TBL_MEMBERS . “ WHERE username = :username”;

try {
$st = $conn- > prepare( $sql );
$st- > bindValue( “:username”, $username, PDO::PARAM_STR );
$st- > execute();
$row = $st- > fetch();
parent::disconnect( $conn );
if ( $row ) return new Member( $row );
} catch ( PDOException $e ) {

parent::disconnect( $conn );
die( “Query failed: “ . $e- > getMessage() );
}
}

public static function getByEmailAddress( $emailAddress ) {
$conn = parent::connect();
$sql = “SELECT * FROM “ . TBL_MEMBERS . “ WHERE emailAddress =
:emailAddress”;

try {
c14.indd 409c14.indd 409 9/21/09 9:14:04 AM9/21/09 9:14:04 AM
410
Part III: Using PHP in Practice
$st = $conn- > prepare( $sql );
$st- > bindValue( “:emailAddress”, $emailAddress, PDO::PARAM_STR );
$st- > execute();
$row = $st- > fetch();
parent::disconnect( $conn );
if ( $row ) return new Member( $row );
} catch ( PDOException $e ) {
parent::disconnect( $conn );
die( “Query failed: “ . $e- > getMessage() );
}
}

These methods should be self - explanatory. You can see that they work in much the same way as

getMember() , which you created in the previous chapter.
Next, add a short method,

getGenres() , that simply retrieves the values in the private $_genres array
property. This will be used for displaying a list of genres for the prospective member to choose from.
Insert it just below the existing
getFavoriteGenreString() method in the class file:
public function getGenres() {
return $this- > _genres;
}

So far the class contains methods for retrieving member records from the members table. Now you ’ re
going to add a new method,
insert() , that adds a new member to the table. Add the following code to
the end of the class file, just before the curly brace that closes the class:

public function insert() {
$conn = parent::connect();
$sql = “INSERT INTO “ . TBL_MEMBERS . “ (
username,
password,
firstName,
lastName,
joinDate,
gender,
favoriteGenre,
emailAddress,
otherInterests
) VALUES (
:username,
password(:password),
:firstName,
:lastName,

:joinDate,
:gender,
:favoriteGenre,
:emailAddress,
:otherInterests
)”;

try {
c14.indd 410c14.indd 410 9/21/09 9:14:05 AM9/21/09 9:14:05 AM
Chapter 14: Manipulating MySQL Data with PHP
411
$st = $conn- > prepare( $sql );
$st- > bindValue( “:username”, $this- > data[“username”], PDO::PARAM_STR );
$st- > bindValue( “:password”, $this- > data[“password”], PDO::PARAM_STR );
$st- > bindValue( “:firstName”, $this- > data[“firstName”], PDO::PARAM_STR );
$st- > bindValue( “:lastName”, $this- > data[“lastName”], PDO::PARAM_STR );
$st- > bindValue( “:joinDate”, $this- > data[“joinDate”], PDO::PARAM_STR );
$st- > bindValue( “:gender”, $this- > data[“gender”], PDO::PARAM_STR );
$st- > bindValue( “:favoriteGenre”, $this- > data[“favoriteGenre”],
PDO::PARAM_STR );
$st- > bindValue( “:emailAddress”, $this- > data[“emailAddress”],
PDO::PARAM_STR );
$st- > bindValue( “:otherInterests”, $this- > data[“otherInterests”],
PDO::PARAM_STR );
$st- > execute();
parent::disconnect( $conn );
} catch ( PDOException $e ) {
parent::disconnect( $conn );
die( “Query failed: “ . $e- > getMessage() );
}

}

If you ’ ve worked through the previous chapter and this chapter so far, there should be no surprises here.

insert() builds an SQL statement to insert the data stored in the current Member object into the
database. Notice that the statement doesn ’ t attempt to insert a value for the
id field, because this is
generated automatically by MySQL.
Then the method prepares the statement with
PDO::prepare() , binds each of the placeholders to the
appropriate value in the
Member object ’ s $data array property, and executes the statement by calling

PDOStatement::execute() . If there were any problems with the insertion, the exception is caught and
displayed and the application exits.
Creating the Registration Script
Now that you ’ ve added the required functionality to your common code file and Member class file,
you ’ re ready to build the registration script itself. Save the following code as
register.php in your

book_club folder:
< ?php

require_once( “common.inc.php” );

if ( isset( $_POST[“action”] ) and $_POST[“action”] == “register” ) {
processForm();
} else {
displayForm( array(), array(), new Member( array() ) );
}


function displayForm( $errorMessages, $missingFields, $member ) {
displayPageHeader( “Sign up for the book club!” );

if ( $errorMessages ) {
c14.indd 411c14.indd 411 9/21/09 9:14:05 AM9/21/09 9:14:05 AM
412
Part III: Using PHP in Practice
foreach ( $errorMessages as $errorMessage ) {
echo $errorMessage;
}
} else {
? >
< p > Thanks for choosing to join our book club. < /p >
< p > To register, please fill in your details below and click Send
Details. < /p >
< p > Fields marked with an asterisk (*) are required. < /p >
< ?php } ? >

< form action=”register.php” method=”post” style=”margin-bottom: 50px;” >
< div style=”width: 30em;” >
< input type=”hidden” name=”action” value=”register” / >

< label for=”username” < ?php validateField( “username”,
$missingFields ) ? > > Choose a username * < /label >
< input type=”text” name=”username” id=”username” value=” < ?php echo
$member- > getValueEncoded( “username” ) ? > ” / >

< label for=”password1” < ?php if ( $missingFields ) echo ‘
class=”error”’ ? > > Choose a password * < /label >

< input type=”password” name=”password1” id=”password1” value=”” / >
< label for=”password2” < ?php if ( $missingFields ) echo ‘
class=”error”’ ? > > Retype password * < /label >
< input type=”password” name=”password2” id=”password2” value=”” /
>


< label for=”emailAddress” < ?php validateField( “emailAddress”,
$missingFields ) ? >
> Email address * < /label >
< input type=”text” name=”emailAddress” id=”emailAddress” value=” < ?php
echo $member- > getValueEncoded( “emailAddress” ) ? > ” / >

< label for=”firstName” < ?php validateField( “firstName”,
$missingFields ) ? > > First name * < /label >
< input type=”text” name=”firstName” id=”firstName” value=” < ?php
echo $member- > getValueEncoded( “firstName” ) ? > ” / >

< label for=”lastName” < ?php validateField( “lastName”,
$missingFields ) ? > > Last name * < /label >
< input type=”text” name=”lastName” id=”lastName” value=” < ?php echo
$member- > getValueEncoded( “lastName” ) ? > ” / >

< label < ?php validateField( “gender”, $missingFields ) ? > > Your
gender: * < /label >
< label for=”genderMale” > Male < /label >
< input type=”radio” name=”gender” id=”genderMale” value=”m” < ?php
setChecked( $member, “gender”, “m” )? >
/ >
< label for=”genderFemale” > Female < /label >

< input type=”radio” name=”gender” id=”genderFemale”
value=”f” < ?php setChecked( $member, “gender”, “f” )? > / >


< label for=”favoriteGenre” > What’s your favorite genre? < /label
>
c14.indd 412c14.indd 412 9/21/09 9:14:05 AM9/21/09 9:14:05 AM

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×