the first level articles, which have no parent. After the tree has been constructed, we simply
call its display function to actually display the list of articles.
Using the treenode Class
The code for the treenode class is shown in Listing 29.5. (You might find it useful at this
stage to look over Chapter 6, “Object Oriented PHP,” to remind yourself how classes work.)
LISTING 29.5 treenode Class from treenode_class.php—The Backbone of the Application
<?
// functions for loading, contructing and
// displaying the tree are in this file
class treenode
{
// each node in the tree has member variables containing
// all the data for a post except the body of the message
var $m_postid;
var $m_title;
var $m_poster;
var $m_posted;
var $m_children;
var $m_childlist;
var $m_depth;
function treenode($postid, $title, $poster, $posted, $children,
$expand, $depth, $expanded, $sublist)
{
// the constructor sets up the member variables, but more
// importantly recursively creates lower parts of the tree
$this->m_postid = $postid;
$this->m_title = $title;
$this->m_poster = $poster;
$this->m_posted = $posted;
$this->m_children =$children;
$this->m_childlist = array();
$this->m_depth = $depth;
// we only care what is below this node if it
// has children and is marked to be expanded
// sublists are always expanded
if(($sublist||$expand) && $children)
{
$conn = db_connect();
Building Web Forums
C
HAPTER 29
29
BUILDING WEB
FORUMS
725
35 7842 CH29 3/6/01 3:34 PM Page 725
$query = “select * from header where parent = $postid order by posted”;
$result = mysql_query($query);
for ($count=0; $row = @mysql_fetch_array($result); $count++)
{
if($sublist||$expanded[ $row[‘postid’] ] == true)
$expand = true;
else
$expand = false;
$this->m_childlist[$count]= new treenode($row[‘postid’],$row[‘title’],
$row[‘poster’],$row[‘posted’],
$row[‘children’], $expand,
$depth+1, $expanded, $sublist);
}
}
}
function display($row, $sublist = false)
{
// as this is an object, it is responsible for displaying itself
// $row tells us what row of the display we are up to
// so we know what color it should be
// $sublist tells us whether we are on the main page
// or the message page. Message pages should have
// $sublist = true.
// On a sublist, all messages are expanded and there are
// no “+” or “-” symbols.
// if this is the empty root node skip displaying
if($this->m_depth>-1)
{
//color alternate rows
echo “<tr><td bgcolor = “;
if ($row%2)
echo “‘#cccccc’>”;
else
echo “‘#ffffff’>”;
// indent replies to the depth of nesting
for($i = 0; $i<$this->m_depth; $i++)
{
echo “<img src = ‘images/spacer.gif’ height = 22
width = 22 alt = ‘’ valign = bottom>”;
}
Building Practical PHP and MySQL Projects
P
ART V
726
LISTING 29.5 Continued
35 7842 CH29 3/6/01 3:34 PM Page 726
// display + or - or a spacer
if ( !$sublist && $this->m_children && sizeof($this->m_childlist))
// we’re on the main page, have some children, and they’re expanded
{
// we are expanded - offer button to collapse
echo “<a href = ‘index.php?collapse=”.
$this->m_postid.”#$this->m_postid’
><img src = ‘images/minus.gif’ valign = bottom
height = 22 width = 22 alt = ‘Collapse Thread’ border = 0></a>”;
}
else if(!$sublist && $this->m_children)
{
// we are collapsed - offer button to expand
echo “<a href = ‘index.php?expand=”.
$this->m_postid.”#$this->m_postid’><img src = ‘images/plus.gif’
height = 22 width = 22 alt = ‘Expand Thread’ border = 0></a>”;
}
else
{
// we have no children, or are in a sublist, do not give button
echo “<img src = ‘images/spacer.gif’ height = 22 width = 22
alt = ‘’valign = bottom>”;
}
echo “ <a name = $this->m_postid ><a href =
‘view_post.php?postid=$this->m_postid’>$this->m_title -
$this->m_poster - “.reformat_date($this->m_posted).”</a>”;
echo “</td></tr>”;
// increment row counter to alternate colors
$row++;
}
// call display on each of this node’s children
// note a node will only have children in its list if expanded
$num_children = sizeof($this->m_childlist);
for($i = 0; $i<$num_children; $i++)
{
$row = $this->m_childlist[$i]->display($row, $sublist);
}
return $row;
}
};
?>
Building Web Forums
C
HAPTER 29
29
BUILDING WEB
FORUMS
727
LISTING 29.5 Continued
35 7842 CH29 3/6/01 3:34 PM Page 727
This class contains the functionality that drives the tree view in this application.
One instance of the treenode class contains details about a single posting and links to all the
reply postings of that class. This gives us the following member variables:
var $m_postid;
var $m_title;
var $m_poster;
var $m_posted;
var $m_children;
var $m_childlist;
var $m_depth;
Notice that the treenode does not contain the body of the article. There is no need to load this
until a user goes to the view_post.php script. We need to try to make this relatively fast, as we
are doing a lot of data manipulation to display the tree list, and need to recalculate when the
page is refreshed, or a button is pressed.
The naming scheme for these variables follows a naming scheme commonly used in OO appli-
cations—starting variables with m_ to remind us that they are member variables of the class.
Most of these variables correspond directly to rows from the header table in our database.
The exceptions are $m_childlist and $m_depth. We will use the variable $m_childlist to
hold the replies to this article. The variable $m_depth will hold the number of tree levels that
we are down—this will be used for creating the display.
The constructor function sets up the values of all the variables, as follows:
function treenode($postid, $title, $poster, $posted, $children,
$expand, $depth, $expanded, $sublist)
{
// the constructor sets up the member variables, but more
// importantly recursively creates lower parts of the tree
$this->m_postid = $postid;
$this->m_title = $title;
$this->m_poster = $poster;
$this->m_posted = $posted;
$this->m_children =$children;
$this->m_childlist = array();
$this->m_depth = $depth;
Building Practical PHP and MySQL Projects
P
ART V
728
35 7842 CH29 3/6/01 3:34 PM Page 728
When we construct the root treenode from display_tree() from the main page, we are actu-
ally creating a -dummy node with no article associated with it. We pass in some initial values:
$tree = new treenode($start, ‘’, ‘’, ‘’, 1, true, -1, $expanded, $sublist);
This creates a root node with a $postid of zero. This can be used to find all the first-level
postings because they have a parent of zero. We set the depth to -1 because this node isn’t
actually part of the display. All the first-level postings will have a depth of zero, and be at the
far left of the screen. Subsequent depths step towards the right.
The most important thing that happens in this constructor is that the children nodes of this
node are instantiated. We begin this process by checking if we need to expand the children
nodes. We only perform this process if a node has some children, and we have elected to dis-
play them:
if(($sublist||$expand) && $children)
{
$conn = db_connect();
We then connect to the database, and retrieve all the child posts, as follows:
$query = “select * from header where parent = $postid order by posted”;
$result = mysql_query($query);
We then fill the array $m_childlist with instances of the treenode class, containing the
replies to the post stored in this treenode, as follows:
for ($count=0; $row = @mysql_fetch_array($result); $count++)
{
if($sublist||$expanded[ $row[‘postid’] ] == true)
$expand = true;
else
$expand = false;
$this->m_childlist[$count]= new treenode($row[‘postid’],$row[‘title’],
$row[‘poster’],$row[‘posted’],
$row[‘children’], $expand,
$depth+1, $expanded, $sublist);
}
This last line will create the new treenodes, following exactly the same process we have just
walked through, but for the next level down the tree. This is the recursive part. A parent tree
node is calling the treenode constructor, passing its own postid as parent, and adding one to
its own depth before passing it.
Each treenode in turn will be created and create its own children until we run out of replies or
levels that we want to expand to.
Building Web Forums
C
HAPTER 29
29
BUILDING WEB
FORUMS
729
35 7842 CH29 3/6/01 3:34 PM Page 729
Building Practical PHP and MySQL Projects
P
ART V
730
After all that’s done, we call the root treenode’s display function (this is back in display_tree()),
as follows:
$tree->display($row, $sublist);
The display() function begins by checking whether this is the dummy root node:
if($this->m_depth>-1)
In this way, the dummy can be left out of the display. We don’t want to completely skip the
root node though. We do not want it to appear, but it needs to notify its children that they need
to display themselves.
The function then starts drawing the table containing the articles. It uses the modulus operator
(
%) to decide what color background this row should have (hence they alternate):
//color alternate rows
echo “<tr><td bgcolor = “;if ($row%2)
echo “‘#cccccc’>”;
else
echo “‘#ffffff’>”;
It then uses the $m_depth member variable to work out how much to indent the current item.
You will see by looking back at the figures that the deeper level a reply is on, the further it is
indented. This is done as follows:
// indent replies to the depth of nesting
for($i = 0; $i<$this->m_depth; $i++)
{
echo “<img src = ‘images/spacer.gif’ height = 22
width = 22 alt = ‘’ valign = bottom>”;
}
The next part of the function works out whether to supply a plus or minus button or nothing
at all:
// display + or - or a spacer
if ( !$sublist && $this->m_children && sizeof($this->m_childlist))
// we’re on the main page, have some children, and they’re expanded
{
// we are expanded - offer button to collapse
echo “<a href = ‘index.php?collapse=”.
$this->m_postid.”#$this->m_postid’
><img src = ‘images/minus.gif’ valign = bottom
height = 22 width = 22 alt = ‘Collapse Thread’ border = 0></a>”;
}
else if(!$sublist && $this->m_children)
{
35 7842 CH29 3/6/01 3:34 PM Page 730
Building Web Forums
C
HAPTER 29
29
BUILDING WEB
FORUMS
731
// we are collapsed - offer button to expand
echo “<a href = ‘index.php?expand=”.
$this->m_postid.”#$this->m_postid’><img src = ‘images/plus.gif’
height = 22 width = 22 alt = ‘Expand Thread’ border = 0></a>”;
}
else
{
// we have no children, or are in a sublist, do not give button
echo “<img src = ‘images/spacer.gif’ height = 22 width = 22
alt = ‘’valign = bottom>”;
}
Next, we display the actual details of this node:
echo “ <a name = $this->m_postid ><a href =
‘view_post.php?postid=$this->m_postid’>$this->m_title -
$this->m_poster - “.reformat_date($this->m_posted).”</a>”;
echo “</td></tr>”;
We change the color for the next row:
// increment row counter to alternate colors
$row++;
After that, there is some code that will be executed by all treenodes, including the root one, as
follows:
// call display on each of this node’s children
// note a node will only have children in its list if expanded
$num_children = sizeof($this->m_childlist);
for($i = 0; $i<$num_children; $i++)
{
$row = $this->m_childlist[$i]->display($row, $sublist);
}
return $row;
Again this is a recursive function call, which calls on each of this node’s children to display
themselves. We pass them the current row color and get them to pass it back when they are fin-
ished with it, so we can keep track of the alternating color.
That’s it for this class. The code is fairly complex. You might like to experiment with running the
application and then come back to look at it again when you are comfortable with what it does.
Viewing Individual Articles
The display_tree() call ends up giving us links to a set of articles. If we click one of these
articles, we will go to the
view_post.php script, with a parameter of the postid of the article
to be viewed. Sample output from this script is shown in Figure 29.7.
35 7842 CH29 3/6/01 3:34 PM Page 731
FIGURE 29.7
We can now see the message body for this posting.
This script shows us the message body, as well as the replies to this message. You will see that
the replies are again displayed as a tree, but completely expanded this time, and without any
plus or minus buttons. This is the effect of the $sublist switch coming into action.
Let’s look at the code for view_post.php, shown in Listing 29.6.
LISTING 29.6 view_post.php—Displays a Single Message Body
<?
// include function libraries
include (‘include_fns.php’);
// get post details
$post = get_post($postid);
do_html_header($post[“title”]);
// display post
display_post($post);
// if post has any replies, show the tree view of them
if($post[‘children’])
{
Building Practical PHP and MySQL Projects
P
ART V
732
35 7842 CH29 3/6/01 3:34 PM Page 732
echo “<br><br>”;
display_replies_line();
display_tree($expanded, 0, $postid);
}
do_html_footer();
?>
This script uses three main function calls to do its job: get_post(), display_post(), and
display_tree().
The get_post() function pulls the function details out of the database. The code for this func-
tion is shown in Listing 29.7.
LISTING 29.7 get_post() Function from discussion_fns.php—Retrieves a Message from
the Database
function get_post($postid)
{
// extract one post from the database and return as an array
if(!$postid) return false;
$conn = db_connect();
//get all header information from ‘header’
$query = “select * from header where postid = $postid”;
$result = mysql_query($query);
if(mysql_numrows($result)!=1)
return false;
$post = mysql_fetch_array($result);
// get message from body and add it to the previous result
$query = “select * from body where postid = $postid”;
$result2 = mysql_query($query);
if(mysql_numrows($result2)>0)
{
$body = mysql_fetch_array($result2);
if($body)
{
$post[‘message’] = $body[‘message’];
}
}
return $post;
}
Building Web Forums
C
HAPTER 29
29
BUILDING WEB
FORUMS
733
LISTING 29.6 Continued
35 7842 CH29 3/6/01 3:34 PM Page 733
This function, given a postid, will perform the two queries required to retrieve the message
header and body for that posting, and put them together into a single associative array which it
then returns.
The results of this function are then passed to the display_post() function from
output_fns.php. This just prints out the array with some HTML formatting, so we have not
included it here.
Finally, the view_post.php script checks whether there are any replies to this article and calls
display_tree() to show them in the sublist format—that is, fully expanded with no plusses or
minuses.
Adding New Articles
After all that, we can now look at how a new post is added to the forum. A user can do this in
two ways: first, by clicking on the New Post button in the index page, and second, by clicking
on the Reply button on the view_post.php page.
These actions both activate the same script, new_post.php, just with different parameters.
Figure 29.8 shows the output from new_post.php when we have reached it by hitting the
Reply button.
Building Practical PHP and MySQL Projects
P
ART V
734
FIGURE 29.8
Replies have the text of the original automatically inserted and marked.
35 7842 CH29 3/6/01 3:34 PM Page 734