Creating a Simple Discussion Forum - Basic Projects - Sams Teach Yourself PHP, MySQL and Apache All in One (2012)

Sams Teach Yourself PHP, MySQL and Apache All in One (2012)

Part V. Basic Projects

Chapter 21. Creating a Simple Discussion Forum

In this chapter, you learn the following:

How to create tables for a simple discussion forum

How to create input forms for a simple discussion forum

How to display a simple discussion forum

In this chapter, you learn the design process behind a simple discussion forum. This includes developing the database tables and user input forms and displaying the results. When broken into pieces like this, such a task seems simple—and it is! The ultimate goal is to understand the concepts and relationships that go into making something like a discussion forum, not to create the world’s most full-functioned system. In fact, you’ll see it’s quite sparse, but it sure is relational.

Designing the Database Tables

Think of the basic components of a forum: topics and posts. A forum—if properly used by its patrons—should have several topics, and each of those topics will have one or more posts submitted by users. Knowing that, you should realize that the posts are tied to the topics through a key field. This key forms the relationship between the two tables.

Think about the requirements for the topics themselves. You definitely need a field for the title, and subsequently you might want fields to hold the creation time and the identification of the user who created the topic. Similarly, think of the requirements for the posts: You want to store the text of the post, the time of its creation, and the identity of person who created it. Most important, you need that key to tie the post to the topic.

The following two table creation statements create these tables, called forum_topics and forum_posts:

CREATE TABLE forum_topics (
topic_title VARCHAR (150),
topic_create_time DATETIME,
topic_owner VARCHAR (150)
CREATE TABLE forum_posts (
topic_id INT NOT NULL,
post_text TEXT,
post_create_time DATETIME,
post_owner VARCHAR (150)


This simple forum example identifies users by their email addresses and does not require any sort of login sequence.

You should now have two empty tables waiting for some input. In the next section, you create the input forms for adding a topic and a post.

Creating an Include File for Common Functions

Previous chapters used an included file of common functions to make your scripts more concise and to help manage information that might change over time, such as a database username and password. The same thing is true in this chapter. Listing 21.1 contains the code shared by the scripts in this chapter.

Listing 21.1 Common Functions in an Included File

1: <?php
2: function doDB() {
3: global $mysqli;
5: //connect to server and select database; you may need it
6: $mysqli = mysqli_connect("localhost", "joeuser",
7: "somepass", "testDB");
9: //if connection fails, stop script execution
10: if (mysqli_connect_errno()) {
11: printf("Connect failed: %s\n", mysqli_connect_error());
12: exit();
13: }
14: }
15: ?>

Lines 2–14 set up the database connection function, doDB. If the connection cannot be made, the script exits when this function is called; otherwise, it makes the value of $mysqli available to other parts of your script.

Save this file as ch21_include.php and place it on your web server. The other code listings in this chapter include this file within the first few lines of the script.

Creating the Input Forms and Scripts

Before you can add any posts, you must add a topic to the forum. It is common practice in forum creation to add the topic and the first post in that topic at the same time, because from a user’s point of view, it doesn’t make much sense to add a topic and then go back, select the topic, and add a reply. You want the process to be as smooth as possible. Listing 21.2 shows the form for a new topic creation, which includes a space for the first post in the topic.

Listing 21.2 Form for Adding a Topic

1: <!DOCTYPE html>
2: <html>
3: <head>
4: <title>Add a Topic</title>
5: </head>
6: <body>
7: <h1>Add a Topic</h1>
8: <form method="post" action="do_addtopic.php">
10: <p><label for="topic_owner">Your Email Address:</label><br/>
11: <input type="email" id="topic_owner" name="topic_owner" size="40"
12: maxlength="150" required="required" /></p>
14: <p><label for="topic_title">Topic Title:</label><br/>
15: <input type="text" id="topic_title" name="topic_title" size="40"
16: maxlength="150" required="required" /></p>
17: <p><label for="post_text">Post Text:</label><br/>
18: <textarea id="post_text" name="post_text" rows="8"
19: cols="40" ></textarea></p>
21: <button type="submit" name="submit" value="submit">Add Topic</button>
23: </form>
24: </body>
25: </html>

Seems simple enough: The three fields shown in the form, which you can see in Figure 21.1, are all you need to complete both tables; your script and database can fill in the rest. Save Listing 21.2 as something like addtopic.html and put it in your web server document root so that you can follow along.


Figure 21.1 The topic-creation form.

To create the entry in the forum_topics table, you use the values from the $_POST['topic_title'] and $_POST['topic_owner'] variables from the input form. The topic_id and topic_create_time fields will be automatically incremented and added via the now() MySQL function, respectively.

Similarly, in the forum_posts table, you use the values of $_POST['post_text'] and $_POST['topic_owner'] from the input form, and the post_id, post_create_time, and the topic_id fields will be automatically incremented or otherwise supplied. Because you need a value for the topic_idfield to be able to complete the entry in the forum_posts table, you know that query must happen after the query to insert the record in the forum_topics table. Listing 21.3 creates the script to add these records to the table.

Listing 21.3 Script for Adding a Topic

1: <?php
2: include 'ch21_include.php';
3: doDB();
5: //check for required fields from the form
6: if ((!$_POST['topic_owner']) || (!$_POST['topic_title']) ||
7: (!$_POST['post_text'])) {
8: header("Location: addtopic.html");
9: exit;
10: }
12: //create safe values for input into the database
13: $clean_topic_owner = mysqli_real_escape_string($mysqli,
14: $_POST['topic_owner']);
15: $clean_topic_title = mysqli_real_escape_string($mysqli,
16: $_POST['topic_title']);
17: $clean_post_text = mysqli_real_escape_string($mysqli,
18: $_POST['post_text']);
20: //create and issue the first query
21: $add_topic_sql = "INSERT INTO forum_topics
22: (topic_title, topic_create_time, topic_owner)
23: VALUES ('".$clean_topic_title ."', now(),
24: '".$$clean_topic_owner."')";
26: $add_topic_res = mysqli_query($mysqli, $add_topic_sql)
27: or die(mysqli_error($mysqli));
29: //get the id of the last query
30: $topic_id = mysqli_insert_id($mysqli);
32: //create and issue the second query
33: $add_post_sql = "INSERT INTO forum_posts
34: (topic_id, post_text, post_create_time, post_owner)
35: VALUES ('".$topic_id."', '".$clean_post_text."',
36: now(), '".$clean_topic_owner."')";
38: $add_post_res = mysqli_query($mysqli, $add_post_sql)
39: or die(mysqli_error($mysqli));
40: //close connection to MySQL
41: mysqli_close($mysqli);
43: //create nice message for user
44: $display_block = "<p>The <strong>".$_POST["topic_title"]."</strong>
45: topic has been created.</p>";
46: ?>
47: <!DOCTYPE html>
48: <html>
49: <head>
50: <title>New Topic Added</title>
51: </head>
52: <body>
53: <h1>New Topic Added</h1>
54: <?php echo $display_block; ?>
55: </body>
56: </html>

Lines 2–3 include the file of user-created functions and call the database connection function. Next, lines 6–10 check for the three required fields needed to complete both tables (the topic owner, a topic title, and some text for the post). If any one of these fields is not present, the user is redirected to the original form. Lines 13–18 create database-safe versions of the contents of those variables.

Lines 21–27 create and insert the first query, which adds the topic to the forum_topics table. Note that no value is entered for the id field in the table; the automatically incrementing value is added by the system per the original table definition. The MySQL now() function is used to timestamp the record with the current time at insertion. The other fields in the record are completed using values from the form.

Line 30 shows the use of a handy function: mysqli_insert_id(). This function retrieves the primary key ID of the last record inserted into the database by this script. In this case, mysqli_insert_id() gets the id value from the forum_topics table, which will become the entry for the topic_idfield in the forum_posts table.

Lines 33–39 create and insert the second query, again using a mixture of information known and supplied by the system. The second query adds the text of the user’s post to the forum_posts table. Lines 44–45 simply create a display string for the user, and the rest of the script rounds out the HTML that is rendered by the browser.

Save this listing as do_addtopic.php—the name of the action in the previous script—and place it in the document root of your web server. Complete the form you created from Listing 21.1 and then submit it, and you should see the New Topic Added message. Figures 21.2 and 21.3 show the sequence of events.


Figure 21.2 Adding a topic and first post.


Figure 21.3 Successful addition of a topic and first post.

In the next section, you put together two more pieces of the puzzle: displaying the topics and posts and replying to a topic.

Displaying the Topic List

Now that you have a topic and at least one post in your database, you can display this information and let people add new topics or reply to existing ones. In Listing 21.4, you take a step back and create a page that lists all the topics in the forum. This page shows the basic information of each topic and provides the user with a link to add a new topic; you have already created the form and script for that. The code in Listing 21.4 represents an entry page for your forum.

Although Listing 21.4 looks like a lot of code, it’s actually many small, simple concepts you’ve already encountered, starting with the include() function and database connection function in lines 2–3.

Listing 21.4 Topic Listing Script

1: <?php
2: include 'ch21_include.php';
3: doDB();
5: //gather the topics
6: $get_topics_sql = "SELECT topic_id, topic_title,
7: DATE_FORMAT(topic_create_time, '%b %e %Y at %r') AS
8: fmt_topic_create_time, topic_owner FROM forum_topics
9: ORDER BY topic_create_time DESC";
10: $get_topics_res = mysqli_query($mysqli, $get_topics_sql)
11: or die(mysqli_error($mysqli));
13: if (mysqli_num_rows($get_topics_res) < 1) {
14: //there are no topics, so say so
15: $display_block = "<p><em>No topics exist.</em></p>";
16: } else {
17: //create the display string
18: $display_block <<<END_OF_TEXT
19: <table>
20: <tr>
21: <th>TOPIC TITLE</th>
22: <th># of POSTS</th>
23: </tr>
26: while ($topic_info = mysqli_fetch_array($get_topics_res)) {
27: $topic_id = $topic_info['topic_id'];
28: $topic_title = stripslashes($topic_info['topic_title']);
29: $topic_create_time = $topic_info['fmt_topic_create_time'];
30: $topic_owner = stripslashes($topic_info['topic_owner']);
32: //get number of posts
33: $get_num_posts_sql = "SELECT COUNT(post_id) AS post_count FROM
34: forum_posts WHERE topic_id = '".$topic_id."'";
35: $get_num_posts_res = mysqli_query($mysqli, $get_num_posts_sql)
36: or die(mysqli_error($mysqli));
38: while ($posts_info = mysqli_fetch_array($get_num_posts_res)) {
39: $num_posts = $posts_info['post_count'];
40: }
42: //add to display
43: $display_block .= <<<END_OF_TEXT
44: <tr>
45: <td><a href="showtopic.php?topic_id=$topic_id">
46: <strong>$topic_title</strong></a><br/>
47: Created on $topic_create_time by $topic_owner</td>
48: <td class="num_posts_col">$num_posts</td>
49: </tr>
51: }
52: //free results
53: mysqli_free_result($get_topics_res);
54: mysqli_free_result($get_num_posts_res);
56: //close connection to MySQL
57: mysqli_close($mysqli);
59: //close up the table
60: $display_block .= "</table>";
61: }
62: ?>
63: <!DOCTYPE html>
64: <html>
65: <head>
66: <title>Topics in My Forum</title>
67: <style type="text/css">
68: table {
69: border: 1px solid black;
70: border-collapse: collapse;
71: }
72: th {
73: border: 1px solid black;
74: padding: 6px;
75: font-weight: bold;
76: background: #ccc;
77: }
78: td {
79: border: 1px solid black;
80: padding: 6px;
81: }
82: .num_posts_col { text-align: center; }
83: </style>
84: </head>
85: <body>
86: <h1>Topics in My Forum</h1>
87: <?php echo $display_block; ?>
88: <p>Would you like to <a href="addtopic.html">add a topic</a>?</p>
89: </body>
90: </html>

Lines 6–11 show the first of the database queries, and this particular one selects all the topic information in order by descending date. In other words, these lines gather the data in such a way that the topic that was created most recently will appear at the top of the list. In the query, notice the use of the date_format() function to create a much nicer date display than the raw value stored in the database.

Line 13 checks for the presence of any records returned by the query. If no records are returned, and therefore no topics are in the table, you want to tell the user. Line 15 creates this message. At this point, if no topics existed, the script would break out of the if...else construct and be over with; the next action would occur at line 63, which is the start of the static HTML. If the script ended here, the message created in line 15 would be printed in line 87.

If you have topics in your forum_topics table, however, the script continues at line 16. At line 18, a block of text is assigned to the $display_block variable, containing the beginnings of an HTML table. Lines 19–23 set up a table with two columns: one for the title and one for the number of posts. At line 26, you begin to loop through the results of the original query.

The while loop in line 26 says that while there are elements to be extracted from the resultset, extract each row as an array called $topic_info, and use the field names as the array element to assign the value to a new variable. So, the first element the script tries to extract is the topic_id field, on line 27. It assigns the value of $topic_info['topic_id'] to the $topic_id variable, meaning that it gets a local value for $topic_id from an array called $topic_info, containing a field called topic_id. Continue doing this for the $topic_title, $topic_create_time, and $topic_ownervariables in lines 28–30. The stripslashes() function removes any escape characters that may have been input into the table at the time of record insertion.

Lines 33–36 create and issue another query, in the context of the while loop, to get the number of posts for that particular topic. In line 43, the script continues the creation of the $display_block string, using the concatenation operator (.=) to make sure that this string is tacked on to the end of the display string we have built so far. In lines 45–47, you create the HTML table column to display the link to the file that will show the topic (showtopic.php) and print the topic owner and creation time.

The second HTML table column, on line 48, shows the number of posts. The script breaks out of the while loop on line 51, and in line 60 adds the last bit to the $display_block string to close the table. The remaining lines print the HTML for the page, including the value of the$display_block string.

If you save this file as topiclist.php and place it in your web server document root, and if you have topics in your database tables, you might see something like Figure 21.4.


Figure 21.4 Topics are available.

Displaying the Posts in a Topic

As you might have guessed, the next item on the task list is to build that showtopic.php file to show the topic’s postings. Listing 21.5 does just that. In this listing, lines 6–9 check for the existence of a value for topic_id in the GET query string. Because you intend to show all the posts within a selected topic, you need to know which topic to use in your query, and this is the manner in which the information is given to you. If a value in $_GET['topic_id'] does not exist, the user is redirected back to the topic listing page, presumably to try again.

Listing 21.5 Script to Show Topic Posts

1: <?php
2: include 'ch21_include.php';
3: doDB();
5: //check for required info from the query string
6: if (!isset($_GET['topic_id'])) {
7: header("Location: topiclist.php");
8: exit;
9: }
11: //create safe values for use
12: $safe_topic_id = mysqli_real_escape_string($mysqli, $_GET['topic_id']);
14: //verify the topic exists
15: $verify_topic_sql = "SELECT topic_title FROM forum_topics
16: WHERE topic_id = '".$safe_topic_id."'";
17: $verify_topic_res = mysqli_query($mysqli, $verify_topic_sql)
18: or die(mysqli_error($mysqli));
20: if (mysqli_num_rows($verify_topic_res) < 1) {
21: //this topic does not exist
22: $display_block = "<p><em>You have selected an invalid topic.<br/>
23: Please <a href=\"topiclist.php\">try again</a>.</em></p>";
24: } else {
25: //get the topic title
26: while ($topic_info = mysqli_fetch_array($verify_topic_res)) {
27: $topic_title = stripslashes($topic_info['topic_title']);
28: }
30: //gather the posts
31: $get_posts_sql = "SELECT post_id, post_text, DATE_FORMAT(post_create_time,
32: '%b %e %Y<br/>%r') AS fmt_post_create_time, post_owner
33: FROM forum_posts
34: WHERE topic_id = '".$safe_topic_id."'
35: ORDER BY post_create_time ASC";
36: $get_posts_res = mysqli_query($mysqli, $get_posts_sql)
37: or die(mysqli_error($mysqli));
39: //create the display string
40: $display_block = <<<END_OF_TEXT
41: <p>Showing posts for the <strong>$topic_title</strong> topic:</p>
42: <table>
43: <tr>
44: <th>AUTHOR</th>
45: <th>POST</th>
46: </tr>
49: while ($posts_info = mysqli_fetch_array($get_posts_res)) {
50: $post_id = $posts_info['post_id'];
51: $post_text = nl2br(stripslashes($posts_info['post_text']));
52: $post_create_time = $posts_info['fmt_post_create_time'];
53: $post_owner = stripslashes($posts_info['post_owner']);
55: //add to display
56: $display_block .= <<<END_OF_TEXT
57: <tr>
58: <td>$post_owner<br/><br/>
59: created on:<br/>$post_create_time</td>
60: <td>$post_text<br/><br/>
61: <a href="replytopost.php?post_id=$post_id">
62: <strong>REPLY TO POST</strong></a></td>
63: </tr>
65: }
67: //free results
68: mysqli_free_result($get_posts_res);
69: mysqli_free_result($verify_topic_res);
71: //close connection to MySQL
72: mysqli_close($mysqli);
74: //close up the table
75: $display_block .= "</table>";
76: }
77: ?>
78: <!DOCTYPE html>
79: <html>
80: <head>
81: <title>Posts in Topic</title>
82: <style type="text/css">
83: table {
84: border: 1px solid black;
85: border-collapse: collapse;
86: }
87: th {
88: border: 1px solid black;
89: padding: 6px;
90: font-weight: bold;
91: background: #ccc;
92: }
93: td {
94: border: 1px solid black;
95: padding: 6px;
96: vertical-align: top;
97: }
98: .num_posts_col { text-align: center; }
99: </style>
100: </head>
101: <body>
102: <h1>Posts in Topic</h1>
103: <?php echo $display_block; ?>
104: </body>
105: </html>

Lines 15–18 show the first of these queries, and this one is used to validate that the topic_id sent in the query string is actually a valid entry by selecting the associated topic_title for the topic in question. If the validation fails the test in line 20, a message is created in lines 22–23, and the script breaks out of the if...else statement and finishes up by printing HTML. This output looks like Figure 21.5.


Figure 21.5 Invalid topic selected.

If, however, the topic is valid, extract the value of topic_title in line 27, again using stripslashes() to remove any escape characters that may have been automatically added upon insertion. Next, the script creates and issues a query in lines 31–37 to gather all the posts associated with that topic in ascending order by time. In this case, newest posts are at the bottom of the list. Line 40 starts a block of text, containing the beginnings of an HTML table. Lines 42–46 set up a table with two columns: one for the author of the post and one for the post text itself. The script stops writing the text block momentarily, and at line 49 begins to loop through the results of the original query.

The while loop in line 49 says that although there are elements to be extracted from the resultset, extract each row as an array called $posts_info, and use the field names as the array element to assign the value to a new variable. So, the first element the script tries to extract is the post_idfield on line 50. It assigns the value of $posts_info['post_id'] to the variable $post_id, meaning that it gets a local value for $post_id from an array called $posts_info, containing a field called post_id. Continue doing this for the $post_text, $post_create_time, and $post_owner variables in lines 51–53. The stripslashes() function is again used to remove any escape characters, and the nl2br() function is used on the value of $posts_info[post_text] to replace all newline characters with line-break characters.

In line 56, the script continues to write to the $display_block string, using the concatenation operator (.=) to make sure that this string is tacked on to the end of the string we have created so far. Lines 58–59 create the HTML table column to display the author and creation time of the post. The second HTML table row, on lines 60–63, shows the text of the post as well as a link to reply to the post. The script breaks out of the while loop on line 65, and on line 75 adds the last bit to the $display_block string to close the table. The remaining lines print the HTML for the page, including the value of the $display_block string.

If you save this file as showtopic.php and place it in your web server document root, and if you have posts in your database tables, you might see something like Figure 21.6.


Figure 21.6 Posts in a topic.

A one-post topic is boring, so let’s finish up this chapter by creating the script to add a post to a topic.

Adding Posts to a Topic

In this final step, you create the replytopost.php script, which contains code that looks similar to the script used to add a new topic. Listing 21.6 shows the code for this all-in-one form and script, which begins with the inclusion of the functions file and the initiation of the database connection on lines 2–3. Although the script performs different tasks depending on the status of the form (whether it’s being shown or submitted), both conditions require database interaction at some point.

Listing 21.6 Script to Add Replies to a Topic

1: <?php
2: include 'ch21_include.php';
3: doDB();
5: //check to see if we're showing the form or adding the post
6: if (!$_POST) {
7: // showing the form; check for required item in query string
8: if (!isset($_GET['post_id'])) {
9: header("Location: topiclist.php");
10: exit;
11: }
13: //create safe values for use
14: $safe_post_id = mysqli_real_escape_string($mysqli, $_GET['post_id']);
16: //still have to verify topic and post
17: $verify_sql = "SELECT ft.topic_id, ft.topic_title FROM forum_posts
18: AS fp LEFT JOIN forum_topics AS ft ON fp.topic_id =
19: ft.topic_id WHERE fp.post_id = '".$safe_post_id."'";
21: $verify_res = mysqli_query($mysqli, $verify_sql)
22: or die(mysqli_error($mysqli));
24: if (mysqli_num_rows($verify_res) < 1) {
25: //this post or topic does not exist
26: header("Location: topiclist.php");
27: exit;
28: } else {
29: //get the topic id and title
30: while($topic_info = mysqli_fetch_array($verify_res)) {
31: $topic_id = $topic_info['topic_id'];
32: $topic_title = stripslashes($topic_info['topic_title']);
33: }
34: ?>
35: <!DOCTYPE html>
36: <html>
37: <head>
38: <title>Post Your Reply in <?php echo $topic_title; ?></title>
39: </head>
40: <body>
41: <h1>Post Your Reply in <?php echo $topic_title; ?></h1>
42: <form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>">
43: <p><label for="post_owner">Your Email Address:</label><br/>
44: <input type="email" id="post_owner" name="post_owner" size="40"
45: maxlength="150" required="required"></p>
46: <p><label for="post_text">Post Text:</label><br/>
47: <textarea id="post_text" name="post_text" rows="8" cols="40"
48: required="required"></textarea></p>
49: <input type="hidden" name="topic_id" value="<?php echo $topic_id; ?>">
50: <button type="submit" name="submit" value="submit">Add Post</button>
51: </form>
52: </body>
53: </html>
54: <?php
55: }
56: //free result
57: mysqli_free_result($verify_res);
59: //close connection to MySQL
60: mysqli_close($mysqli);
62: } else if ($_POST) {
63: //check for required items from form
64: if ((!$_POST['topic_id']) || (!$_POST['post_text']) ||
65: (!$_POST['post_owner'])) {
66: header("Location: topiclist.php");
67: exit;
68: }
70: //create safe values for use
71: $safe_topic_id = mysqli_real_escape_string($mysqli, $_POST['topic_id']);
72: $safe_post_text = mysqli_real_escape_string($mysqli, $_POST['post_text']);
73: $safe_post_owner = mysqli_real_escape_string($mysqli, $_POST['post_owner']);
75: //add the post
76: $add_post_sql = "INSERT INTO forum_posts (topic_id,post_text,
77: post_create_time,post_owner) VALUES
78: ('".$safe_topic_id."', '".$safe_post_text."',
79: now(),'".$safe_post_owner."')";
80: $add_post_res = mysqli_query($mysqli, $add_post_sql)
81: or die(mysqli_error($mysqli));
83: //close connection to MySQL
84: mysqli_close($mysqli);
86: //redirect user to topic
87: header("Location: showtopic.php?topic_id=".$_POST['topic_id']);
88: exit;
89: }
90: ?>

Line 6 checks to see whether the form is being submitted. If $_POST does not have a value, the form has not yet been submitted, and it must be shown. Before showing the form, however, you must check for that one required item; lines 8–11 check for the existence of a value for post_id in theGET query string. If a value in $_GET['post_id'] does not exist, the user is redirected back to the topic listing page.

If you made it past the check for a value in $_GET['post_id'], lines 17–22 create and issue a complicated-looking query that gets the values of the topic_id and topic_title fields from the forum_topics table, based on the only value that you know: a now-database-safe (thanks to line 14) value of $_GET['post_id']. This query both validates the existence of the post and gets information you will need later in the script. Lines 24–27 act on the results of this validity test, again redirecting the user back to the topiclist.php page if the test fails.

If the value of $_GET['post_id'] represents a valid post, you extract the value of topic_id and topic_title in lines 30–33, again using stripslashes() to remove any escape characters. Next, the script prints to the screen the entirety of the form for adding a post, and that’s it for this script until the user clicks the form submission button. In the form, you see that the action is $_SERVER['PHP_SELF'] on line 42, indicating that this script will be recalled into action. A hidden field in line 49 holds the information that needs to be passed along to the next iteration of the script.

Moving on to line 62, this block of code is executed when the script is reloaded and $_POST contains a value. This block checks for the presence of all required fields from the form (lines 64–68) and then, if they are all present, issues the query to add the post to the database (lines 76–81) using safe values created in lines 71–73. After the post is added to the database, the user is redirected to the showtopic.php page (lines 87–88), using the appropriate query string to display the active topic.

If you save this file as replytopost.php and place it in your web server document root, try it out and you may see something like Figures 21.7 and 21.8.


Figure 21.7 Preparing to add a post.


Figure 21.8 A post was added to the list.


To take an idea from inception through to fruition, you should follow a design process. This process essentially says, “Think before you act.” Discuss rules, requirements, and objectives; then create the final version of your normalized tables.

In this chapter, you saw how forums are hierarchical in nature: Forums contain topics; topics contain posts. You cannot have a topic without a post, and posts do not exist in forums without belonging to a topic. You applied this knowledge to the creation of tables to hold forum topics and posts, and used PHP scripts to create the input and display pages for these items.


Q. What if I want multiple forums? This sequence assumes that only one forum is available.

A. If you want to have multiple forums in your discussion board, create a table called forums (or something to that effect) containing fields for an ID, name, and perhaps a forum description. Then, in the forum_topics and forum_posts tables, add a field called forum_id so that these elements lower in the hierarchy are tied to the master forum. Be sure to amend the SQL queries for record insertion to account for the value of the forum_id.

Next, instead of starting your display at the topic level, begin it at the forum level. Just as you created a script to display topics, create a script to show the forums. The link to the forum display would contain the forum_id, and the page itself would show all the topics within that forum.


The workshop is designed to help you review what you’ve learned and begin putting your knowledge into practice.


1. How is the topic ID value passed to the showtopic.php script?

2. What else, besides telling the user that the topic was successfully added, could we do at the end of the do_addtopic.php script?

3. Why does the script use the mysqli_real_escape_string() function on values from forms?


1. Through the $_GET superglobal, named as the the value of $_GET['topic_id'].

2. Just as with the replytopost.php script, we could eliminate the message display and simply redirect the user to the topic she just created, showing the new topic and post in all its glory.

3. The mysqli_real_escape_string() function guards against SQL injection attacks by preparing “safe” strings for insertion into the database tables.


1. You’ll notice that none of these pages are really tied together with any sort of navigation. Take these basic framework scripts and apply some navigational flow to them. Make sure that users can always add a topic or return to the topic list from any given page, for example.

2. Use the information provided in the Q&A section to integrate and display multiple forums into your tidy little discussion board. While you’re at it, apply some text styles and colors to jazz up these bare-bones examples.