677 Solution Overview We will build simple interfaces to enable saving of articles. When we load the list of articles for viewing, we will load the headers of each article into a tree_node PHP class. Each tree_node will contain an article’s headers and a set of the replies to that article. The replies will be stored in an array. Each reply will itself be a tree_node, that can contain an array of replies to that article, which are themselves tree_nodes, and so on. This continues until we reach the so-called leaf nodes of the tree, the nodes that do not have any replies.We will then have a tree structure that looks like the one in Figure 29.1. Some terminology:The message that we are replying to can be called the parent node of the current node.Any replies to the message can be called the children of the current node. If you imagine that this tree structure is like a family tree, this will be easy to remember. The first article in this tree structure—the one with no parent—is sometimes called the root node. Note This can be unintuitive because we usually draw the root node at the top of diagrams, unlike the roots of real trees. To b uild and display this tree structure, we will write recursive functions. (We discussed recursion in Chapter 5,“Reusing Code and Writing Functions.”) We decided to use a class for this structure because it’s the easiest way to build a com- plex, dynamically expanding data structure for this application. It also means we have quite simple, elegant code to do something quite complex. Solution Overview To r eally understand what we have done with this project, it’s probably a good idea to work through the code, which we’ll do in a moment.There is less bulk in this applica- tion than in some of the others, but the code is a bit more complex. There are only three real pages in the application. We will have a main index page that shows all the articles in the forum as links to the articles. From here, you will be able to add a new article, view a listed article, or change the way the articles are viewed by expanding and collapsing branches of the tree. (More on this in a minute.) From the article view, you will be able to post a reply to that article or view the existing replies to that article. The new article page enables you to enter a new post, either a reply to an existing message, or a new unrelated message. The system flow diagram is shown in Figure 29.2. 35 525x ch29 1/24/03 3:36 PM Page 677 678 Chapter 29 Building Web Forums Figure 29.2 There are three main parts of the blah-blah forum system. A summary of the files in this application is shown in Table 29.1. Table 29.1 Files in the Web Forum Application Name Type Description index.php Application The main page users will see when they enter the site. Contains an expandable and collapsible list of all the articles on the site. new_post.php Application Form used for posting new articles. store_new_post.php Application Stores articles entered in the new_post.php form. view_post.php Application Displays an individual post and a list of the replies to that post. treenode_class.php Library Contains the treenode class, which we will use to display the hierarchy of posts. include_fns.php Library Brings all the other function libraries for this application together (the other Library-type files listed here). data_valid_fns.php Library Data validation functions. db_fns.php Library Database connectivity functions. discussion_fns.php Library Functions for dealing with storing and retrieving postings. output_fns.php Library Functions for outputting HTML. create_database.sql SQL SQL to set up the database required for this appli- cation. Let’s go ahead and look at the implementation. Designing the Database There are a few attributes we’ll need to store about each article posted to the forum: the person who wrote it, called the poster; the title of the article; when it was posted; and the article body.We will therefore need a table of articles.We’ll create a unique ID for each article, called the postid. Add a new articleView an article Article list (different views) reply 35 525x ch29 1/24/03 3:36 PM Page 678 679 Designing the Database Each article needs to have some information about where it belongs in the hierarchy. We could store information about an article’s children with the article. However, each article can have many replies, so this can lead to some problems in database construction. As each article can only be a reply to one other, it is easier to store a reference to the parent article, that is, the article that this article is replying to. That gives us the following data to store for each article: n postid:A unique ID for each article n parent:The postid of the parent article n poster:The author of this article n title:The title of this article n posted:The date and time that the article was posted n message:The body of the article We will add a couple of optimizations to this. When we are trying to determine whether an article has any replies, we will have to run a query to see whether any other articles have this article as a parent.We will need this information for every post that we list.The fewer queries we have to run, the faster our code will run.We can remove the need for these queries by adding a field to show whether there are any replies.We will call this field children and make it effectively Boolean—the value will be 1 if the node has children, and 0 if it does not. There is always a price to pay for optimizations. Here we are choosing to store redundant data.As we are storing the data in two ways, we must be careful to make sure that the two representations agree with each other.When we add children, we must update the parent. If we allow the deletion of children, we need to update the parent node to make sure the database is consistent. In this project we are not going to build a facility for deleting articles, so we will avoid half of this problem. If you decide to extend this code, bear this issue in mind. It is worth noting that some databases would help us out a little more here. If we were using Oracle, it could maintain relational integrity for us. Using MySQL, which does not support triggers or foreign key constraints, we need to write our own checks and balances to make sure that data still makes sense each time we add or delete a record. We will make one other optimization:We will separate the message bodies from the other data and store them in a separate table.The reason for this is that this attribute will have the MySQL type text.Having this type in a table can slow down queries on that table. Because we will do many small queries to build the tree structure, this would slow it down quite a lot.With the message bodies in a separate table, we can just retrieve them when a user wants to look at a particular message. MySQL can search fixed size records faster than variable sized records. If we need to use variable sized data, we can help by creating indexes on the fields that will be used to search the database. For some projects, we would be best served by leaving the text field in the same record as everything else and specifying indexes on all the columns that we 35 525x ch29 1/24/03 3:36 PM Page 679 680 Chapter 29 Building Web Forums will search on. Indexes take time to generate though, and the data in our forums is likely to be changing all the time, so we would need to regenerate our indexes frequently. We will also add an area attribute in case we later decide to implement multiple chats with the one application.We won’t implement this here, but this way it is reserved for future use. Given all these considerations, the SQL to create the database for the forum database is shown in Listing 29.1. Listing 29.1 create_database.sql—SQL to Create the Discussion Database create database discussion; use discussion; create table header ( parent int not null, poster char(20) not null, title char(20) not null, children int default 0 not null, area int default 1 not null, posted datetime not null, postid int unsigned not null auto_increment primary key ); create table body ( postid int unsigned not null primary key, message text ); grant select, insert, update, delete on discussion.* to discussion@localhost identified by 'password'; You can create this database structure by running this script through MySQL as follows: mysql -u root -p < create_database.sql You will need to supply your root password.You should probably also change the pass- word we have set up for the discussion user to something better. To understand how this structure will hold articles and their relationship to each other, look at Figure 29.3. As you can see, the parent field for each article in the database holds the postid of the article above it in the tree.The parent article is the article that is being replied to. You can also see that the root node, postid 1, has no parent. All new topics of discus- sion will be in this position. For articles of this type, we store their parent as a 0 (zero) in the database. 35 525x ch29 1/24/03 3:36 PM Page 680 681 Viewing the Tree of Articles Figure 29.3 The database holds the tree structure in a flattened relational form. Viewing the Tree of Articles Next, we need a way of getting information out of the database and representing it back in the tree structure.We will do this with the main page, index.php.For the purposes of this explanation, we have input some sample posts via the article posting scripts new_post.php and store_new_post.php.We will look at these in the next section. We will cover the article list first because it is the backbone of the site. After this, everything else will be easy. Figure 29.4 shows the initial view of the articles in the site that a user would see. postid: 3 postid: 4 postid: 5 postid: 1 postid: 1 parent: 0 postid: 2 parent: 1 postid: 3 parent: 1 postid: 4 parent: 2 postid: 5 parent: 2 postid: 2 Database representation Tree representation Figure 29.4 The initial view of the article list shows the articles in “collapsed” form. 35 525x ch29 1/24/03 3:36 PM Page 681 . articles. store_new_post .php Application Stores articles entered in the new_post .php form. view_post .php Application Displays an individual post and a list of the replies to that post. treenode_class .php Library. running this script through MySQL as follows: mysql -u root -p < create_database.sql You will need to supply your root password.You should probably also change the pass- word we have set up for. posts. include_fns .php Library Brings all the other function libraries for this application together (the other Library-type files listed here). data_valid_fns .php Library Data validation functions. db_fns.php