. 57
( 132 .)


// display any child topics of this child, at the next
// higher level
$output .= display_kids($r_topic_id, $level+1);

if ($output)
if ($level)
// if not the first level and at least one child
// was found,
// display it as an unordered list
$output = ul_list(array(˜contents™=>$output));
// this is the first child record:
// - print out a header
$output = “<b>Comments:</b><br>\n$output”;
return $output;

This function displays information about a given topic. If no topic_id is indicated,
a list of the root-level topics is displayed.
Chapter 10: Threaded Discussion 327

function display_topic ($topic_id=0, $show_kids=1, $level=0)
$topic_id = (int)$topic_id;
$show_kids = (int)$show_kids;
$level = (int)$level;

The following portion executes if no topic_id is indicated. It displays the root-
level topics.

if (!$topic_id)
// no topic_id given - get all root topics
$query = ˜select topic_id, name from topics where topic_id =
$result = my_query($query);

The query in the preceding snippet gets all the root-level topics. The while...
loop directly following prints each topic as an HTML anchor, something like <a
href=”index.php?topic_id=1”>Topic name</a>. When it™s finished, it returns
from the function.

while (list($r_topic_id,$r_name) = mysql_fetch_row($result))
// print the name of each topic as
// a link to this script, passing in
// the topic_id
print paragraph(anchor_tag(
, $r_name

If a topic_id is available, the following query gets the parent and root of the
indicated topic_id. An outer join ensures that the information regarding the cur-
rent topic is returned by the query, even if the parent or root topic has gone missing.

$query = “select distinct current.parent_id
, current.root_id
, current.name
, current.description
328 Part IV: Not So Simple Applications

, current.author
, date_format(current.create_dt,™%b %e %Y %r™) as
, date_format(current.modify_dt,™%b %e %Y %r™) as
, current.author_addr_id
, parent.name as parent_name
, root.name as root_name
from topics current
left join topics as parent
on current.parent_id = parent.topic_id
left join topics as root
on current.root_id = root.topic_id
where current.topic_id = $topic_id “
$result = my_query($query);
$r = mysql_fetch_assoc($result);
if ($r === FALSE)
print paragraph(˜<b>Error:</b> No such topic.™);

if (empty($author)) { $author = ˜[no name]™; }

if ($root_id != $topic_id && $root_id != $parent_id)
// if the root topic is something other than the current
// topic or its immediate parent, print out its name
// as a link to it
if ($root_name == ˜™) { $root_name = ˜[no topic name]™; }
print paragraph(
, anchor_tag(˜index.php?topic_id=™.$root_id
, $root_name

If a parent topic exists, the name of the topic is printed, along with a link to it.

if (!empty($parent_name))
Chapter 10: Threaded Discussion 329

// if an immediate parent was found, print out its name
// as a link to it
print paragraph(
, anchor_tag(˜index.php?topic_id=™.$parent_id
, $parent_name

// print out the current topic
print paragraph(
“<b>$name</b> by <b>$author</b> (ID#$author_addr_id) on
print paragraph($description);

if ($show_kids)
// print out a link to where the user to reply to
// the current topic
print paragraph(
, ˜<b>Reply to this</b>™

// now display any children of the current topic
print paragraph(display_kids($topic_id, $level));

// return information retrieved about the current topic
return array(˜root_id™=>$root_id, ˜parent_id™=>$parent_id,


This function inserts the data taken from a form into the database. As we men-
tioned earlier, we are taking the IP address from the $_SERVER[˜REMOTE_ADDR™]
variable and using it to generate a unique ID number, which is also inserted into the
Many of the fields (such as $root_id) are coming from hidden form fields. And
root_id is set to 0 if the user is attempting to create a new top-level topic. In those
cases the parent_id needs to be set to the same value as the topic_id.
330 Part IV: Not So Simple Applications

function create_topic()
static $_defaults = array(
˜name™ => ˜[no name]™
, ˜description™ => ˜[no comments]™
, ˜parent_id™ => 0
, ˜root_id™ => 0
, ˜author™ => ˜[no author]™
static $_simple = array();
$args = func_get_args();
$p = parse_arguments($args, $_simple, $_defaults);

// run the topic name, description, and author through the
// cleanup_text() function (defined in /book/functions/base.php)
// to remove HTML tags and other special characters.

$name = cleanup_text($p[˜name™]);
$description = cleanup_text($p[˜description™]);
$author = cleanup_text($p[˜author™]);

// we want to know that the same person is having a conversation
// with himself, but these days storing real IP addresses is a
// bit hinky. this serves our purpose just as well.
$author_addr =
$author_addr = crypt($author_addr,$author_addr);
$result = my_query(“select author_addr_id from author_addrs
where author_addr = ˜$author_addr™”
if (mysql_num_rows($result))
list($author_addr_id) = mysql_fetch_row($result);
“insert into author_addrs (author_addr) values
$author_addr_id = mysql_insert_id();
Chapter 10: Threaded Discussion 331

// insert the new record into the topics table
$query = sprintf(
“insert into topics
(name,description, parent_id, root_id, author,
values (˜%s™,™%s™,%d,%d,™%s™,™%s™)

, mysql_real_escape_string($name)
, mysql_real_escape_string($description)
, $p[˜parent_id™]
, $p[˜root_id™]
, mysql_real_escape_string($author)
, $author_addr_id

// begin transaction


$topic_id = mysql_insert_id();
if ($p[˜root_id™] == 0)
// if the root_id is zero, that means that this
// topic is itself a root topic. set the root_id
// column of its database record to that ID value
// (a root topic is its own root)
˜update topics set root_id = topic_id where root_id = 0™

// end transaction

return $topic_id;

This function simply inserts the data into the database. All the information is
coming from an HTML form.
In going through these functions, you might notice that you never check to see
if the result of a query is false. You can get away with this because you aren™t get-
ting away with it, really; you™re just checking in the reusable my_query() function.
332 Part IV: Not So Simple Applications

if ($result === FALSE)
// if there was an error executing the query, write out the
// details to the error log
$private_error = ˜ack! query failed: ˜


. 57
( 132 .)