31.7 添加新文章
了解了以上所有操作后,我们现在可以来看看如何将一篇新文章加入到论坛中去。用户可以使用两种方法来完成该项操作:第一,通过在索引页面点击"New Post"按钮,第二,通过在view_post.php页面点击"Reply"按钮。
这些动作都将触发相同的脚本,new_post.php,只是带有不同的参数。图31-8显示通过点击"Reply"按钮而得到的new_post.php脚本的输出结果。
图 31-8 在回复文章中,父文章的文本将自动插入和标记
首先,让我们查看上图所示的URL:
http://localhost/phpmysql4e/chapter31/new_post.php?parent=5
传递给parent参数值应该是新文章父节点的postid。如果点击了"New Post"按钮而不是"Reply"按钮,将在URL中得到parent=0的字符串。
其次,可以看到对于一个回复,最初文章的文本被插入进来,而且前面都用“>”字符标记了,这一点与大多数的邮件和新闻阅读程序一样。
第三,可以看到回复消息的默认标题为最初标题加上前缀"Re:"。下面,我们来看看产生该输出的代码,如程序清单31-8所示。
程序清单31-8 new_post.php——允许用户对一个已有帖子输入或创建一篇新文章
<?php
include('include_fns.php');
$title=$_POST['title'];
$poster=$_POST['poster'];
$message=$_POST['message'];
if(isset($_GET['parent'])){
$parent=$_GET['parent'];
}else{
$parent=$_POST['parent'];
}
if(!$area){
$area=1;
}
if(!$error){
if(!$parent){
$parent=0;
if(!$title){
$title='New Post';
}
}else{
//get post name
$title=get_post_title($parent);
//append Re:
if(strstr($title,'Re:')==false){
$title='Re:'.$title;
}
//make sure title will still fit in db
$title=substr($title,0,20);
//prepend a quoting pattern to the post you are replying to
$message=add_quoting(get_post_message($parent));
}
}
do_html_header($title);
display_new_post_form($parent,$area,$title,$message,$poster);
if($error){
echo"<p>Your message was not stored.</p>
<p>Make sure you have filled in all fields and
try again.</p>";
}
do_html_footer();
?>
在完成一些初始化设置之后,该脚本将检查父节点是否为0。如果是0,则表示该文章是一个新主题,而接下来需要完成的操作就相对少些。
如果是一个回复($parent变量是某篇已有文章的postid),以上脚本程序将继续执行并且设置标题和最初消息的文本,如下所示:
//get post name
$title=get_post_title($parent);
//append Re:
if(strstr($title,'Re:')==false){
$title='Re:'.$title;
}
//make sure title will still fit in db
$title=substr($title,0,20);
//prepend a quoting pattern to the post you are replying to
$message=add_quoting(get_post_message($parent));
这里用到的函数包括get_post_title()、get_post_message()和add_quoting()。这些函数都来自discussion_fns.php函数库。它们分别如程序清单31-9、程序清单31-10和程序清单31-11所示。
程序清单31-9 discussion_fns.php函数库中的get_post_title()函数——从数据库中得到某消息的标题
function get_post_title($postid){
//extract one post's name from the database
if(!$postid){
return'';
}
$conn=db_connect();
//get all header information from'header'
$query="select title from header where postid='".$postid."'";
$result=$conn->query($query);
if($result->num_rows!=1){
return'';
}
$this_row=$result->fetch_array();
return$this_row[0];
}
程序清单31-10 discussion_fns.php函数库中的get_post_message()函数——从数据库中获得某消息的正文
function get_post_message($postid){
//extract one post's message from the database
if(!$postid){
return'';
}
$conn=db_connect();
$query="select message from body where postid='".$postid."'";
$result=$conn->query($query);
if($result->num_rows>0){
$this_row=$result->fetch_array();
return$this_row[0];
}
}
这两个函数分别从数据库中获得特定文章的标题和正文。
程序清单31-11 discussion_fns.php函数库中的add_quoting()函数——用“>”符号缩进一则消息的正文
function add_quoting($string,$pattern='>'){
//add a quoting pattern to mark text quoted in your reply
return$pattern.str_replace("\n","\n$pattern",$string);
}
add_quoting()函数将重新格式化字符串,用一个符号开始原始文本的每一行,在默认情况下,该符号为“>”。
用户在回复文章中完成所需输入并且点击"Post"按钮后,将进入store_new_post.php脚本程序。图31-9所示的是该脚本的一个示例输出。
图 31-9 新发表的文章在树形结构中显示出来
在上图中,新发表的文章在这行下面Re:using gd?-Laura-Julie-09:36 07/20/2008。不仅如此,该页面看起来还像普通的index.php页面。
下面,我们来看看store_new_post.php的代码,如程序清单31-12所示。
程序清单31-12 store_new_post.php——在数据库中存入新的文章
<?php
include('include_fns.php');
if($id=store_new_post($_POST)){
include('index.php');
}else{
$error=true;
include('new_post.php');
}
?>
可以看到,这是一段很短的脚本程序。它的主要任务是调用store_new_post()函数。该页面没有它自身的可见内容。如果保存成功,我们会看到索引页面。否则,将回到new_post.php页,这样,用户可以重试。store_new_post()函数如程序清单31-13所示。
程序清单31-13 discussion_fns.php函数库中的store_new_post()函数——在数据库中保存并检验新发表的文章
function store_new_post($post){
//validate clean and store a new post
$conn=db_connect();
//check no fields are blank
if(!filled_out($post)){
return false;
}
$post=clean_all($post);
//check parent exists
if($post['parent']!=0){
$query="select postid from header where
postid='".$post['parent']."'";
$result=$conn->query($query);
if($result->num_rows!=1){
return false;
}
}
//check not a duplicate
$query="select header.postid from header,body where
header.postid=body.postid and
header.parent=".$post['parent']."and
header.poster='".$post['poster']."'and
header.title='".$post['title']."'and
header.area=".$post['area']."and
body.message='".$post['message']."'";
$result=$conn->query($query);
if(!$result){
return false;
}
if($result->num_rows>0){
$this_row=$result->fetch_array();
return$this_row[0];
}
$query="insert into header values
('".$post['parent']."',
'".$post['poster']."',
'".$post['title']."',
0,
'".$post['area']."',
now(),
NULL
)";
$result=$conn->query($query);
if(!$result){
return false;
}
//note that our parent now has a child
$query="update header set children=1 where postid='".$post['parent']."'";
$result=$conn->query($query);
if(!$result){
return false;
}
//find our post id,note that there could be multiple headers
//that are the same except for id and probably posted time
$query="select header.postid from header left join body
on header.postid=body.postid
where parent='".$post['parent']."'
and poster='".$post['poster']."'
and title='".$post['title']."'
and body.postid is NULL";
$result=$conn->query($query);
if(!$result){
return false;
}
if($result->num_rows>0){
$this_row=$result->fetch_array();
$id=$this_row[0];
}
if($id){
$query="insert into body values
($id,'".$post['message']."')";
$result=$conn->query($query);
if(!$result){
return false;
}
return$id;
}
}
这是个很长的函数,但它并不太复杂。它之所以长,是因为插入一篇文章就意味着要标题表和正文表进行插入操作,并要在标题表中更新父文章行以表明它现在有子文章了。
以上就是该Web论坛应用程序的所有代码。