Chapter 8Hosting Discussions with a Web Forum In This Chapter Understanding how a Web forum works Building a database to store and organize messages Building and designing the forum Desi
Trang 1296 Part IV: Building Other Useful Applications
L ISTING7-15: (Continued)
$page[“body_text”] = “Welcome to our Intranet “ “where each department shares content with “ “the whole company You can update your “ “own departments content too with our simple “ “interface.<p>Vist the departments’ “
“home pages: $body_links”;
break;
$dept_id = $_GET[‘dept_id’];
try {
$department = Department::findById($dept_id); #83 }
catch(Exception $e) {
$page[“left_nav”] = “$dept_name Content”;
$page[“body_text”] = “$dept_name - $dept_desc”;
$dept_id = $_GET[‘dept_id’];
$type_id = $_GET[‘type_id’];
try {
Trang 2Chapter 7: Building a Content Management System
$department = Department::findById($dept_id); #122 }
catch(Exception $e) {
$content_types = ContentType::findAll(); #134 }
catch(Exception $e) {
echo $e->getMessage();
exit();
} foreach ($content_types as $content_type) {
$link = “$base_url?dept_id=$dept_id”
“&type_id=” $content_type->getId() “&browse_level=content”;
$page[“col_headers”][“create_date”] = “Created On”;
$page[“col_headers”][“created_by”] = “Created By”;
$page[“col_headers”][“last_upd_date”] =
“Last Updated On”;
$page[“col_headers”][“last_upd_by”] =
Trang 3298 Part IV: Building Other Useful Applications
L ISTING7-15: (Continued)
“Last Updated By”;
$page[“left_nav_header”] = “Content”;
$page[“top”] = “$dept_name - $area_name”;
$trail = “ - <a href=’$base_url?dept_id=$dept_id”
>del</a>]”;
} } foreach ($_GET as $name => $value)
Trang 4Following is a description of the numbered lines of code that appear in
CompanyHome-OO.php, shown in listing 7-15:
#11 Lines 11 to 14 ensure that the class definitions are included so thatthe object model outlined earlier can be put into action
#31 Here the OO login application from Chapter 4 is leveraged
#48 The home page display is handled in this block of the switch
$page[“body_text”] = “<center><u>Add Downloads</u>”; for ($i = 0; $i < 3; $i++)
<input type=’reset’ name=’action’
value =’Reset Form’>
<input type=’submit’ name=’action’
value =’Cancel’>
<input type=’submit’ name=’action’
value =’Save Changes’>
</center>”;
$page[“top”] = “ Edit/Create”;
} else {
}
?>
Trang 5#51 The static method call finds all the departments in the intranet Thearray returned from this function contains Departmentobjects.
#59 Each Departmentobject is examined in this loop so that the ment name and description can be displayed on the home page
depart-#79 The department-level display is handled in this block of the switch
statement
#83 At this point in the program’s execution, the user has clicked a cific department The $departmentvariable becomes an instantiation
spe-of the Departmentclass when the findByIdfunction returns
#90 Lines 90 and 91 retrieve some details about the department
#97 Line 97 gets the list of content types in the CMS, each content typebeing represented by a ContentTypeobject
#98 Begins a loop through all the ContentTypeobjects The objects’details will be used to build the links to the content areas for theselected department
#117 The content list display is handled in this block of the switch
statement
#122 This static function call retrieves an object representing the selected
department
#134 The static function call will find all the content types in the system.
#151 At line 151 the list of content items is added to a page-level variable
so that company-OO.incwill be able to build the display of all theobjects’ data
#153 Begins a block of code that will display an informational message if
there are no content items for the selected department and contenttype
#183 The block of code executed when a user is looking at the details for a
single content item
#186 Lines 186 to 189 get details for the display from objects.
#198 If the $content_idvariable contains a value, the details of the tent item are retrieved by using the finder function at line 199
con-#200 Lines 200 to 206 show the details of the content item being extracted
from the object using the getter functions
#207 The downloadable files associated with the selected content item are
retrieved
#209 Begins the loop that builds links to the downloadable files by using
the details from the ContentDownloadobjects
300 Part IV: Building Other Useful Applications
Trang 6Writing company-OO.inc, the main display code
Here the objects set up in CompanyHome-OO.php— the file shown in ing 7-16 — are leveraged to build the HTML display Other than the use ofthese objects, not much else is different from the procedural version of thisfile, company.inc
List-301
Chapter 7: Building a Content Management System
L ISTING 7-16: B UILDING THE HTML D ISPLAY
<?php /* File: company-OO.inc
* Desc: Contains the code for a Web page that displays
* company and department data.
<h3 align=”center”><?php echo $page[‘top’] ?></h3>
<div style=”font-size: 70%; font-weight: bold”>
<?php echo $trail ?></div>
echo “<tr><td >”
“<a href=\”$link\”>$label<p><p></td></tr>\n”;
}
if (sizeof($page[“left_nav_links”]) == 0) echo “<i>no items yet</i>”;
Trang 7302 Part IV: Building Other Useful Applications
include(“fields_content.inc”);
include(“content_form.inc”);
} else if (sizeof($page[“data_rows”]) > 0) {
echo “<table cellspacing=’3’ cellpadding=’3’
echo “<td nowrap>” $content_item->getCreationDate() “</th>\n”;
echo “<td nowrap>” $content_item->getCreatedBy() “</th>\n”;
echo “<td nowrap>” $content_item->getLastUpdDate() “</th>\n”;
echo “<td nowrap>” $content_item->getLastUpdBy()
echo “<th nowrap>[“;
if ($admin) {
echo “<a href=\”Admin-OO.php?action=delete”
“&dept_id=$dept_id&type_id=$type_id&content_id=” $content_item->getId() “\”>delete</a>\n”;
} echo “<a href=\”CompanyHome-OO.php?”
“&dept_id=$dept_id&type_id=$type_id&content_id=”
$content_item->getId() Æ
“&browse_level=details&edit=false\”>” “view</a>\n”;
Trang 8Following is a description of the numbered lines of code that appear in
CompanyHome-OO.php(shown in Listing 7-16):
#68 Lines 68 to 79 show the use of the objects to get the content itemdetails For each column displayed in the HTML table, a different func-tion is used to get the value Keep in mind that the data in this objectmaps back to data stored in the database However, this display codedoesn’t know about the data, how it is stored, or how it is retrieved Aslong as the objects can be leveraged, the display can be constructed
Writing fields_content.inc and content_form.inc
The fields_content.incand content_form.incfiles from the proceduralcode section can be reused for the object-oriented approach to coding theCMS application
Writing Admin-OO.php, the data manipulation code
The data manipulation code is simpler than the procedural code in this chapter because the SQL code isn’t executed in the administrative PHP code Instead, the administrative PHP, Admin-OO.php, operates on objects
303
Chapter 7: Building a Content Management System
if ($admin) {
echo “<a href=\”CompanyHome-OO.php?”
“&dept_id=$dept_id&type_id=$type_id&content_id=”
$content_item->getId() Æ
“&browse_level=details&edit=true\”>” “edit</a>\n”;
} echo “]</th></tr>\n”;
} echo “</table>\n”;
} echo $page[“body_text”];
Trang 9The saveand deletefunctions of the objects are used to modify the lying data This differs from the procedural Admin.phpfile, where SQL tomanipulate the data is defined within the PHP itself The OO code removesthe need to have SQL in the administrative PHP file In Listing 7-17, the PHPcode simply uses method calls to manipulate the underlying data.
under-304 Part IV: Building Other Useful Applications
L ISTING 7-17: U PDATING THE D ATABASE AND S AVING U PLOADED F ILES
<?php /* File: Admin-OO.php
* Desc: Perform any data manipulation tasks, like
* creating, editing, or deleting content items.
$content_date = date(“Y-m-d”, $content_date);
$last_upd_date = date(“Y-m-d”, time());
if (!isset($created_by))
$created_by = $_SESSION[“user_name”];
$last_upd_by = $_SESSION[“user_name”];
switch ($action) {
$content_item = ContentItem::findById($content_id);
if (isset($content_item)) try
{
$content_item->delete();
} catch(Exception $e) {
Trang 10case “Save Changes”:
$content_item = ContentItem::findById($content_id);
} catch(Exception $e) {
$content_item = new ContentItem(NULL,
$title, $description, $content_date,
$download = new ContentDownload($content_id,
$file_name);
$file_id = $download->save();
Trang 11Following is a description of the numbered lines in Admin-OO.php, shown inListing 7-17:
#8 The two types of data that can be changed by users of the CMS, sented by the ContentItemand ContentDownloadclasses, are used
if(!mkdir($dest_dir, 0700, TRUE)) die (“Can’t archive attachments to $dest_dir”);
}
if (!file_exists($dest_file)) {
if (!move_uploaded_file($file[“tmp_name”],
$dest_file)) die (“Can’t archive attachments to $dest_dir”);
} } break;
try {
$download = ContentDownload::findById($download_id);
$download->delete();
} catch(Exception $e) {
echo $e->getMessage();
exit();
} break;
Trang 12#37 The block of code beginning at line 37 uses the ContentItemclass’sstatic finder function to get the single content item object that theuser wishes to delete The object’s deletefunction is called to removethe underlying data from the database Admin-OO.phpdoesn’t need toknow how this is accomplished because the object-oriented approachallows such details to be encapsulated in the class’s code.
#65 Begins a block of code that updates an existing content item
#81 Begins a block of code that creates a new content item
#89 Begins a loop that saves any uploaded files to the database Again,the details of how the information is saved to the database isabstracted from the code in Admin-OO.php
#114 This is where the details for a single downloadable file are looked up.
After the information is located, it’s removed via the
ContentDownloadobject’s deletefunction
Enhancing the Content Management System
The CMS you see in this chapter is very generic It supports a number of tent types, but the user interface, the data, and the object model don’t differmuch for each content type The object model could be further developed sothat, perhaps, a Newsobject and an Eventobject would have more attributesthat are relevant to the object model’s specific content type For instance, an
con-Eventcontent type could be implemented so that users could register forone or more events
A well-designed CMS should organize content so that, if the volume of datagrows, browsing through and searching for content doesn’t become tedious
Paging is a useful feature for simplifying navigation through long lists of tent items Also, a keyword search is a nice tool that enables users to quicklylocate a number of related but distinct content items
con-307
Chapter 7: Building a Content Management System
Trang 13308 Part IV: Building Other Useful Applications
Trang 14Chapter 8
Hosting Discussions with a Web Forum
In This Chapter
Understanding how a Web forum works
Building a database to store and organize messages
Building and designing the forum
Designing a procedural application that hosts discussions
Using inheritance to simplify your object-oriented code
AWeb forum is a popular way to let your Web site visitors communicate
with you and with each other A forum is like a community bulletinboard where one visitor can leave a message and other visitors can read thatmessage and reply to it with messages of their own In a lively forum, you cantypically find questions (and answers); tips and techniques; reviews (forproducts, books, restaurants, and so on); and links to other resources
Designing the Forum Application
The basic function of a Web forum is to store, organize, and display messages.When you navigate to a forum site (by using a Web browser, such as Firefox orKonqueror), you see a page that displays a list of forums For example, if thesite hosts discussions related to household pets, you might see forums namedCats, Dogs, Reptiles, and Others Underneath each forum, you might see a list
of topics owned by the forum Within the Cats forum, for example, you might
see topics such as Cat Breeds, Health Issues, Feeding, and Bad Habits TheDogs forum might hold a similar set of topics If you look inside the Reptilesforum, you might find Amphibians, Snakes, Lizards, Housing, and Feeding.When you click a topic, your browser moves to a new page that displays a list
of the threads owned by the topic A thread is a collection of messages, all of
which share the same topic From this page, you can click a thread to displaythe messages owned by that thread or you can click the Start a New Thread
Trang 15button to create a new thread When you navigate to a specific thread, you see
a list of all the messages in that thread Each message displays the author (that
is, the e-mail address of the user who posted the message); the date/time thatthe message was posted; and the body of the message To reply to a message,just click the Reply button next to that message
The heart of the forum application is a set of tables that organize the
mes-sages created by your users When a user posts a new message, the text of
the message is stored as a single row in a table that I call Post Each Post
contains a bit of bookkeeping information (such as the author of the sage, the date the message was posted, and so on) To organize the messages
mes-in your forum, you can add a few layers on top of the Posttable to groupmessages into similar areas of discussion Each Postbelongs to a single
Thread Each Threadbelongs to a single Topic Each Topicbelongs to asingle Forum All together, you need four tables to organize messages the way I describe: Forum, Topic, Thread, and Post
Forums and topics are different from threads and posts in several respects.You, the forum administrator, create the forums and topics Your users createthreads and posts To create a new forum, you simply add a row to the Forum
table To create a new topic, add a row to the Topictable When a user posts
a message, he can start a new thread or reply to an existing thread To start anew thread, the user clicks the Start a New Thread button, types in a subjectfor the thread, and types in the body of the message When he clicks the PostMessage button, a new row is inserted into the Threadtable and a new row
is inserted into the Posttable To add a message to an existing thread, theuser clicks the Reply To button and types in the body of the message (Notethat he doesn’t have to enter a subject for the message — the subject is inher-ited from the message that he’s replying to.) When he clicks the Post Replybutton, a new row is inserted into the Posttable (and the related Threadisupdated to indicate the date of the most recent message)
You need to consider the security of your forum Will you allow any visitor to
start new threads? Must a user register (and log in) before she can reply to
an existing thread? The forum application that you develop in this chaptertakes a middle-of-the-road approach to security Only a forum administratorcan add new forums and new topics Anyone can start a new thread or reply
to an existing one Throughout this chapter, I point out a few changes thatyou can make to adjust the security policy to fit your needs
Creating the Forum Database
This application has four layers of organization Looking from the top down,you start with a forum, which contains one or more topics Each topic contains
310 Part IV: Building Other Useful Applications
Trang 16a collection of zero or more threads (A topic is empty until someone starts athread.) Each thread contains a collection of one or more posts, all sharingthe same subject Looking at the forum structure from the bottom up, eachmessage is owned by a single thread, each thread is owned by a single topic,and each topic is owned by a single forum.
Designing the Forum database
The sample application in this chapter uses a database named Forum Theforum application defines four tables: Forum, Topic, Thread, and Post Eachtable contains an idcolumn — every row is uniquely identified by its id Tolink the tables together into a hierarchy, every Post, Thread, and Topiccon-tains the unique identifier of its parent For example, a Postis owned by a
Thread: If you look inside a Post, you see a column named parent_thread
that contains the idof the parent Thread In a similar fashion, each Thread
contains a parent_topicand each Topiccontains a parent_forum
Designing the Forum table
The Forumtable contains information about each forum Table 8-1 shows thelayout of the Forumtable
Table 8-1 Database Table: Forum
Variable Name Type Description
id INTEGER UNSIGNED Unique identifier for forum
(primary key)
description TEXT Description of forum
The Forumtable is straightforward; it exists only to organize similar topicsinto meaningful groups Each row contains a unique identifier (id), a name,and a description The idcolumn is defined as an auto_incrementcolumn
so that the MySQL server assigns a unique value to each row The forum name
is required, but the descriptionis optional
Designing the Topic table
The Topictable contains information about each topic Table 8-2 shows thelayout of the Topictable
311
Chapter 8: Hosting Discussions with a Web Forum
Trang 17Table 8-2 Database Table: Topic
Variable Name Type Description
parent_forum INTEGER UNSIGNED Forum to which this topic
belongs
id INTEGER UNSIGNED Unique identifier for topic
(pri-mary key)
description TEXT Description of topic
The Topictable is very similar to the Forumtable; the Topictable exists only
to organize similar threads into meaningful groups Every row in the Topic
table belongs to some row in the Forumtable The parent_forumin anygiven Topicidentifies the owner of the Topic(that is, Topic.parent_forum
will contain the Forum.idvalue of the Forumthat owns the Topic) The id
column serves as a unique identifier for the Topicand, like the Forum.id
column, the MySQL server assigns a value to this column each time you add
a new Topic Topic.nameis required and Topic.descriptionis optional
Designing the Thread table
The Threadtable contains information about each thread The Threadtable
is shown in Table 8-3
Table 8-3 Database Table: Thread
Variable Name Type Description
parent_topic INTEGER UNSIGNED Topic to which this thread
last_post TIMESTAMP Date (and time) of most recent
post in this thread
312 Part IV: Building Other Useful Applications
Trang 18The Threadtable is a bit more complex than the Forumand Topictables.
Each Threadbelongs to a Topicand the Thread.parent_topiccolumn contains the Topic.idof the owner Like the Forumand Topictables, each
Threadis uniquely identified by a server-assigned id When a user starts anew Thread, she must provide a subject The repliescolumn contains acount of the number of messages in the thread (minus one — the first mes-sage in a thread doesn’t count as a reply) The last_postcolumn containsthe time and date of the most recent message added to any given Thread.You add a new record to the Threadtable each time a user starts a new dis-cussion You update Thread.repliesand Thread.last_posteach time amessage is added to the thread
Designing the Post table
The Posttable contains information about each post The Posttable isshown in Table 8-4
Table 8-4 Database Table: Post
Variable Name Type Description
parent_thread INTEGER UNSIGNED Thread to which this post
belongs
in_reply_to INTEGER UNSIGNED If this message is the first
message in a thread,
in_reply_tois NULL; otherwise, in_reply_to
contains the ID of some other Post
Id INTEGER UNSIGNED Unique identifier for post
body TEXT Text of message
Each message is stored as a single row in the Posttable Notice that you aren’tstoring a subject with each Post— instead, the subject is stored in the Thread
that owns the Post The in_reply_tocolumn contains the idof another Post
If a Postis the first message in a Thread, in_reply_towill be NULL; otherwise,the Postmust be a reply to some other message The authorcolumn containsthe name of the user who posted the message The datecolumn stores thetime and date that the message was added — the MySQL server automaticallytimestamps each row when it is added it to the table
313
Chapter 8: Hosting Discussions with a Web Forum
Trang 19The parent_thread, parent_topic, and parent_forumcolumns link theforum tables together into a hierarchy.
You can find the thread that owns a given post by executing a query such
as SELECT * FROM Thread WHERE Thread.id = Post.parent_thread.Similarly, you can find the topic that owns a thread with the query
SELECT * FROM Topic WHERE Topic.id = Thread.parent_topic.You can find the forum that owns a topic with the query SELECT * FROMForum WHERE Forum.id = Topic.parent_forum
To find all the topics belonging to a forum, use SELECT * FROM TopicWHERE Topic.parent_forum = Forum.id
To find all the threads belonging to a topic, use SELECT * FROM ThreadWHERE parent_topic = Topic.id
To find all messages that belong to a thread, use a query such as SELECT
* FROM Thread, Post WHERE parent_thread = Thread.id
If you want to tighten the security policy for your site, you must create anadditional table that keeps track of registered users The Login applicationthat you develop in Chapter 4 provides everything you need to register andauthenticate visitors
Building the forum tables
The following SQL query creates this database:
CREATE DATABASE Forum
The following CREATE TABLEstatements create all the tables you need forthe forums application
CREATE TABLE Forum (
id INTEGER UNSIGNED NOT NULL auto_increment, name VARCHAR(100) NOT NULL,
description TEXT, PRIMARY KEY(id) );
CREATE TABLE Topic ( parent_forum INTEGER UNSIGNED NOT NULL,
id INTEGER UNSIGNED NOT NULL auto_increment, name VARCHAR(100) NOT NULL,
description TEXT, PRIMARY KEY(id) );
314 Part IV: Building Other Useful Applications
Trang 20CREATE TABLE Thread ( parent_topic INTEGER UNSIGNED NOT NULL,
id INTEGER UNSIGNED NOT NULL auto_increment, subject TEXT NOT NULL,
replies INTEGER UNSIGNED, last_post TIMESTAMP,
PRIMARY KEY(id) );
CREATE TABLE Post ( parent_thread INTEGER UNSIGNED NOT NULL, in_reply_to INTEGER UNSIGNED,
id INTEGER UNSIGNED NOT NULL auto_increment, author VARCHAR(100) NOT NULL,
date TIMESTAMP, body TEXT, PRIMARY KEY(id) );
Accessing the forum tables
To interact with the MySQL server, your PHP scripts use the mysql (or mysqli)API that comes with PHP By using the mysql functions, you can connect to aMySQL server, execute queries and other SQL statements, and retrieve results
from the server When you fetch (or retrieve) a row of data from the MySQL
server, you can ask PHP to deliver the row in many different forms If you callthe mysql_fetch_arrayfunction, you get an array of values, indexed bycolumn number If you call mysql_fetch_assoc, you get an associative array,indexed by column name You can also ask mysql to return a row in the form
of an object (The resulting object has one field for each column in the row
The name of the field corresponds to the name of the column.)
In this application, I fetch each row of data into an associative array An ciative array offers three advantages over an enumerated array:
asso- When you access a member of an associate array, your code is
self-documenting $thread[‘parent_topic’]is much more descriptivethan $thread[2]
Your code is self-maintaining If you change the SELECTstatement thatcreates a result set, $thread[‘parent_topic’]always refers to a
parent_topic, but $thread[2]might refer to the wrong column
(Consider what would happen if you add a column to the beginning ofthe SELECTlist.)
You can easily convert an associative array into a set of “normal”
vari-ables by using the extract function For example, if you have an
asso-ciative array that contains columns named parent_topic, name, and
description, you can extract the array, and you’ll have three new
vari-ables: $parent_topic, $name, and $description
315
Chapter 8: Hosting Discussions with a Web Forum
Trang 21PHP provides two different sets of MySQL functions: mysql functions andmysqli functions The mysqli functions were developed to allow the use offeatures that were added in MySQL version 4.1 You can use the mysql func-tions with version 4.1, but you don’t have access to the newer features Themysql or mysqli extension is activated when PHP is installed You must usePHP 5 to use the mysqli functions.
Because MySQL 4.1 is now the recommended version on the MySQL Web site,
I use the MySQL Improved (mysqli) functions in this chapter I use the cedural functions when building the procedural programs I use the object-oriented classes when building the object-oriented programs
pro-If you’re using PHP 4 or for other reasons want to use the mysql functions —rather than the mysqli functions — you might need to make small changes tothe syntax The mysqli functions are very similar to the mysql functions, butsome differences exist I explain the PHP and MySQL versions in Chapter 1.The syntax differences are shown in Appendix C More information about thefunctions is available in the PHP manual at www.php.net/manual/en/ref.mysqli.phpand www.php.net/manual/en/ref.mysql.php
In this application, I have stored the information needed by the PHP mysqlfunctions in a separate file called forumVars.inc This file is stored in adirectory outside my Web space, for security reasons The file contains infor-mation similar to the following:
Adding data to the database
To cut down on the number of forms required by this application, you need
to maintain two of these tables (Forumand Topic) directly Visitors to yoursite maintain the other two tables (Threadand Post) by using the forms that
I describe later in this chapter
To create a new forum, simply insert (by using an INSERTquery) a new rowinto the Forumtable, like this:
INSERT INTO Forum (name, description) VALUES (‘Cats’, ‘All things kitty’);
316 Part IV: Building Other Useful Applications
Trang 22Notice that you can omit the idcolumn and MySQL will assign a uniquenumber for you If you let MySQL assign an idfor your Forum, you can findthe value that the server chose by calling the LAST_INSERT_IDfunction Tocreate a new topic, you need to know the idof the Forumthat should ownthe topic Creating a new topic is almost as easy as creating a new forum; justinsert (INSERT) a new row into the Topictable:
INSERT INTO Topic (parent_forum, name, description ) VALUES ( 1, ‘Cat Breeds’, ‘Discussions regarding different breeds’ );
INSERT INTO Topic (parent_forum, name, description ) VALUES ( 1, ‘Health Issues’, ‘Keeping your felines healthy and happy’ );
You must create at least one Forumand one Topicbefore your forum site will
be usable
Building the Forum Web Pages
A visitor interacts with your forum application through a series of HTML Webpages Some of the pages display simple HTML tables; others display HTMLforms that the user must complete in order to proceed to the next page Each
page is dynamically created, which means that the data displayed to the user
comes from one or more tables stored in the MySQL server
Designing the Forum Web pages
The forum application displays the following five Web pages:
Forums: This page lists all the forums and the topics available in each
forum
Threads: This page lists all the threads in the selected topic This page
is displayed when the user clicks a Topic link in the Forums page
Messages: This page displays all the messages in a thread This page is
displayed when the user clicks a thread link in the Threads page
New Message: This page displays a form where the user can create and
post a message This page is displayed when the user clicks the Start aNew Thread link
Reply: This page displays a form where the user can reply to a message.
This page is displayed when the user clicks the reply button
The same files are used in both the procedural and object-oriented code todisplay all five pages The next few sections describe the pages and the filesthat display the pages
317
Chapter 8: Hosting Discussions with a Web Forum