文章教程

10.2.1系统功能分析及数据库设计

9/17/2020 9:38:49 PM 人评论 次浏览

10.2 设计“网络留言板”实例

很多网站都拥有自己的留言板,用于实现访问者与管理者之间的交流和沟通。简单的留言板可以通过文件存储的方式实现,而复杂的留言板则需要有后台数据库的支持。本节介绍一个由PHP + MySQL开发的网络留言板,本实例保存在下载源代码的“10\book”目录下。

10.2.1 系统功能分析及数据库设计

网络留言板需要有一个系统管理员用户,负责维护和管理留言板的内容,回复访问者提出的问题。本实例假定系统管理员用户为Admin,默认密码为pass。

访问者无须登录就可以通过本系统留言,而且可以查看所有公开的留言内容(只给管理员查看的信息除外)。

Admin用户可以对留言信息进行管理,包括删帖、发布公告等。公告信息始终显示在留言板的最上方,即通常所说的置顶信息。

本实例使用的数据库为book。数据库中包含以下两个表。

1.表Content

表Content用来保存留言的标题和内容,表结构如表10-3所示。

表10-3 表Content的结构

figure_0189_0270

本实例支持用户选择头像,所有头像图片保存在应用程序目录的 images目录下,Logo 字段中只保存文件名,不必包含路径信息。

2.表Users

表 Users 用来保存系统用户信息,本实例中只有一个用户,即系统管理员 Admin。表 Users的结构如表10-4所示。

表10-4 表Users的结构

figure_0189_0271

创建数据库的脚本如下:

CREATE DATABASE IF NOT EXISTS book

COLLATE 'gb2312_chinese_ci';

USE book;

CREATE TABLE IF NOT EXISTS Users (

UserName VARCHAR(50) PRIMARY KEY,

UserPwd VARCHAR(50),

ShowName VARCHAR(50));

CREATE TABLE IF NOT EXISTS Content (

ContId INT AUTO_INCREMENT PRIMARY KEY,

Subject VARCHAR(200) NOT NULL,

Words VARCHAR(1000) NOT NULL,

UserName VARCHAR(50) NOT NULL,

Face VARCHAR(50),

Email VARCHAR(50),

Homepage VARCHAR(50),

CreateTime DATETIME,

UpperId INT

);

INSERT INTO Users VALUES('admin', 'pass', 'admin');

此段脚本保存为下载源代码的10\book\book.sql。默认的管理员用户为admin,密码为pass。

10.2.2 定义数据库访问类

为了体现出面向对象的程序设计思路,本书实例中将每个表的数据库操作都封装到类中,类与表同名。

本实例中包含两个表,即表Users和表Content。因此创建Users和Content两个类,它们保存在下载源代码的“10\book\Class”目录下。

类Users的成员函数如表10-5所示。

表10-5 类Users的成员函数

figure_0190_0272

类Content的成员函数如表10-6所示。

表10-6 类Content的成员函数

figure_0190_0273

本章稍后将结合具体的使用来介绍这些代码。

10.2.3 设计留言板的主页

留言板的主页为 index.PHP。普通用户和管理员的权限不同,普通用户不需要登录,即可以查看所有留言,或使用本系统留言。留言板主页的界面如图10-6所示。

figure_0191_0274

图10-6 留言板首页

主页最基本的功能显示所有的留言信息,首先需要准备要显示的留言记录集,主要代码如下:

<?PHP

$UserName = $_POST['UserName'];

if($UserName != "")

include('ChkPwd.php'); //检查用户名和密码

include('Show.php'); //引用显示留言内容的函数

include('Class\Content.php'); //引用Content类的定义

$objContent = new Content(); //定义Content对象,用于访问表Content

$pageSize = 5; //设置每页显示留言记录的数量

$pageNo = (int)$_GET['Page']; //获取当前显示的页码

$recordCount = $objContent->GetRecordCount();

//处理不合法的页码

if($pageNo < 1)

$pageNo = 1;

//计算总页数

if( $recordCount ){

//如果记录总数量小于每页显示的记录数量,则只有一页

if( $recordCount < $pageSize ){

$pageCount = 1;

}

//取记录总数量不能整除每页显示记录的数量,则页数等于总记录数量除以每页显示记录数量的结果取整再加1

if( $recordCount % $pageSize ){

$pageCount = (int)($recordCount / $pageSize) + 1;

}

else { //如果没有余数,则页数等于总记录数量除以每页显示记录的数量

$pageCount = $recordCount / $pageSize;

}

}

else{ //如果结果集中没有记录,则页数为0

$pageCount = 0;

}

if($pageNo > $pageCount)

$pageNo = $pageCount;

?>

程序的主要执行过程如下。

(1)设置每页显示留言记录的数量$pageSize,默认数值为5。

(2)从参数Page中获取当前的页码,保存在变量$pageNo中。如果没有参数,则页码为1。

(3)定义Content 对象objContent,调用objContent-> GetRecordCount()获取所有留言记录的总数,保存在变量$pageCount中。

(4)计算总页数$pageCount,计算方法已经在10.1.3小节中介绍,请读者参照理解。

为了让网页中的文字格式统一,这里定义了样式main,代码如下:

<!-

.main  { font-size: 10pt }

-->

样式main的字体大小为10pt。在文字上套用样式的代码如下:

class = "main"

在index.php中,显示留言内容和页码控制链接的代码如下:

<tr><td height="161" class="main"><?PHP ShowList($pageNo, $pageSize); ?></td></tr>

<tr> <td height="15"> </td></tr>

<tr>

<td height="13" class="main"         <?PHPbackground="images/b3.gif">ShowPage($pageCount, $pageNo); ?></td>

</tr>

在上面的程序中,<tr>…</tr>和<td>…</td>用于构造一个表格,并在表格中调用 ShowList()函数显示留言内容,调用ShowPage()函数显示页码控制链接。关于如何显示留言内容的代码将在10.2.4小节介绍。

在主页中,管理员可以输入用户名和密码进行登录,相关表单的定义代码如下:

<?PHP

if(!$_SESSION['Passed']) {

?>

<form method="POST" action="<?PHP $_SERVER['PHP_SELF'] ?>" name="myform">

&nbsp;<font size="2"> 用户名 : </font><input type="text" name="UserName"size="12">&nbsp;&nbsp;

密 码: <input type="password" name="UserPwd" size="12"> <input type="submit"value="登录" name="B1">&nbsp;

<?PHP

}

else {

echo("<b>欢迎管理员光临!</b>");

}

?>

表单的处理脚本为$_SERVER['PHP_SELF'],即当前脚本自身index.php。当$_SESSION['Passed']等于False时才输出上面的表单,否则输出“欢迎管理员光临!”。

在index.php中,程序首先获取表单域UserName的值,如果UserName有值,则使用include()函数包含ChkPwd.php进行身份认证。ChkPwd.php的代码如下:

<?PHP

include('class\Users.php');  // 包含Users类

$user = new Users();

session_start();

//如果尚未定义Passed对象,则将其定义为False,表示没有通过身份认证

if(!isset($_SESSION['Passed'])) {

$_SESSION['Passed'] = False;

}

//如果$_SESSION['Passed']=False,则表示没有通过身份验证

if($_SESSION['Passed']==False) {

//读取从表单传递过来的身份数据

$UserName = $_POST['UserName'];

$UserPwd = $_POST['UserPwd'];

if($UserName == "")

$Errmsg = "请输入用户名和密码";

else {

//验证用户名和密码

if(!$user->verify($UserName, $UserPwd)) {

?>

<script language="javascript">

alert("用户名或密码不正确!");

</script>";

<?PHP

}

else { //登录成功 ?>

<script language="javascript">

alert("登录成功!");

</script>

<?PHP

$_SESSION['Passed'] = True;

$_SESSION['UserName'] = $UserName;

$_SESSION['ShowName'] = $user->ShowName;

//$_SESSION['ShowName'] = $row[2];

}

}

}

//经过登录不成功,则画出登录表单MyForm

if(!$_SESSION['Passed']) {

?>

<script language="javascript">

history.go(-1);

</script>

<?PHP } ?>

程序调用$user->verify()函数验证用户名和密码,如果通过验证,则将$_SESSION['Passed'] 设置为True,并将用户名和显示名保存在Session变量中。

10.2.4 显示主题留言

本留言板中的留言可以分为两种类型,一种是主题留言,另一种是回复留言。在首页按主题留言的发表时间显示,而回复留言则在主题留言的内容部分显示。

为了更方便地显示主题留言,本实例在 Show.PHP 中定义了两个函数,即 ShowPage()和ShowList()。

ShowPage()函数的功能是显示页码信息。因为论坛使用分页显示的方法显示主题留言,所以需要在留言列表的上面显示页码及翻页链接,包括下面的功能:

• 通过下拉菜单使用户可以直接跳转到指定页码的页面;

• 通过“第一页”、“上一页”、“下一页”和“最后一页”超级链接,使用户跳转到指定的页面;

• 显示论坛的当前页码和总页数。

ShowPage()的代码如下:

<?PHP

// $recordCount表示返回结果集中的总页数,$pageNo表示当前页码

function ShowPage( $pageCount, $pageNo ) {

echo("<table width=738> <tr> <td align=right class=main>");

//显示第一页,如果当前页就是第一页,则不生成链接

if($pageNo>1)

echo("<A HREF=index.php?Page=1>第一页</A>&nbsp;&nbsp;");

else

echo("第一页&nbsp;&nbsp;");

//显示上一页,如果不存在上一页,则不生成链接

if($pageNo>1)

echo("<A HREF=index.php?Page=" . ($pageNo-1) . ">上一页</A>&nbsp;&nbsp;");

else

echo("上一页&nbsp;&nbsp;");

//显示下一页,如果不存在下一页,则不生成链接

if($pageNo<>$pageCount)

echo("<A HREF=index.php?Page=" . ($pageNo+1) . ">下一页</A>&nbsp;&nbsp;");

else

echo("下一页&nbsp;&nbsp;");

//显示最后一页,如果当前页就是最后一页,则不生成链接

if($pageNo <> $pageCount)

echo("<A HREF=index.php?Page=" . $pageCount . ">最后一页</A>&nbsp;&nbsp;");

else

echo("最后一页&nbsp;&nbsp;");

//输出页码

echo($pageNo . "/" . $pageCount . "</td></tr></table>");

}

ShowPage()有两个参数,$pageCount表示总页数,$pageNo表示当前的页码。在显示“第一页”、“上一页”、“下一页”和“最后一页”超级链接时,需要根据$pageNo 的值决定是否显示超级链接。如果当前页是第一页,则“第一页”和“上一页”不显示超级链接;如果当前页是最后一页,则“下一页”和“最后一页”不显示超级链接。这些超级链接都转向到index.php,并将指定的页码作为参数传递到Page。

ShowList()函数的功能是以表格的形式主题留言,包括下面的功能:

• 显示主题、作者、创建日期和时间、最后回复日期和时间、人气等信息;

• 优先显示“置顶”的帖子;

• 留言按最后回复日期和时间降序排列,这样最后回复的帖子将出现在最上面。

ShowList()的代码如下:

<?PHP

function ShowList( $pageNo, $pageSize ) {

?>

<div align="center">

<center>

<table border="1" width="738" bordercolor="#3399FF" cellspacing="0" cellpadding="0"height="46" bordercolorlight="#FFCCFF" bordercolordark="#CCCCFF">

<?PHP

$existRecord = False;

$objContent = new Content();

$results = $objContent->load_content_byPage($pageNo, $pageSize);

//使用while语句显示所有$results中的留言数据

while($row = $results->fetch_row()) {

$existRecord = True;

?> <tr>

<td width="148" height="16" class="main" align=center> <br>

<img border="0" src="images/<?PHP echo($row[4]); /*输出 Face 字段的内容,即头像文件*/ ?>.gif" width="100" height="100"><br>

<?PHP echo($row[3]); /*输出UserName字段的值*/?><br><br>

<a href="<?PHP echo($row[6]); /*输出Homepage字段的值*/?>" target=_blank>

<img border="0" src="images/homepage.gif" width="16" height="16"></a>

<a href="mailto:<?PHP echo($row[5]); /*输出Email字段的值*/ ?>">

<img border="0" src="images/email.gif" width="16" height="16"></a><br>

<?PHP if($_SESSION["UserName"] <> "") {?>

<a href=newRec.php?UpperId=<?PHP echo($row[0]); /*ContId*/ ?>target=_blank onclick="return newwin(this.href)">回复</a>

<a href=deleteRec.php?ContId=<?PHP echo($row[0]); /*ContId*/ ?>target=_blank onclick="return newwin(this.href)">删除</a>

<?PHP } /* end of if */ ?>

</td>

<td width="584" height="16" class="main" align="left" valign="top">

<br><b>标题:<?PHP echo($row[1]);/* Subject */ ?>&nbsp;&nbsp;&nbsp;时间:<?PHP echo($row[7]); /* CreateTime */ ?></b><hr><br>

<?PHP

echo($row[2]); /* Words */

//下面用于显示所有回复留言

$content = new Content(); //定义Content对象

$sub_results = $content->load_content_byUpperid($row[0]);

while($subrow = $sub_results->fetch_row()) {

echo("<BR><BR><BR>"); ?>&nbsp;&nbsp;&nbsp;

<img border="0" src="images/<?PHP echo($subrow[4]); /* Face */ ?>.gif"width="50" height="50">

<?PHP echo($subrow[3]); /* UserName") */ ?>

<a href="<?PHP echo($subrow[6]); /*homepage*/ ?>" target=_blank>

<img border="0" src="images/homepage.gif" width="16" height="16"></a>

<a href="mailto:<?PHP echo($subrow[5]); /* email */ ?>">

<img border="0" src="images/email.gif" width="16" height="16"></a>&nbsp;&nbsp;&nbsp;

<b>&nbsp;&nbsp;&nbsp; 标题 :<?PHP echo($subrow[1]); /*Subject */?>&nbsp;&nbsp;&nbsp;时间: <?PHP echo($subrow[7]); /* CreateTime */ ?></b><hr><br>

&nbsp;&nbsp;&nbsp; <?PHP echo($subrow[2]); /* Words */ ?>

<?PHP

} // end of while

?>

</td>

</tr>

<?PHP

} // end of while

if(!$existRecord) {

?>

<tr>

<td width="148" height="16" align=center class="main">没有留言数据</td>

</tr>

<?PHP

} // end of if

echo("</table></center></div>");

} // end of function

?>

ShowList()函数也有两个参数,$pageSize 表示每页中允许显示的留言记录数量,$pageNo 表示当前的页码。程序定义 Content 类对象 objContent,并调用$objContent->load_content_byPage()函数获取当前页面中包含的留言记录,然后使用 while 语句依次处理并显示每条留言记录。留言记录的显示被分为两个区域,左侧单元格中显示用户头像、姓名、邮箱和主页地址,右侧单元格显示留言主题、留言时间、留言内容和回复留言信息。只有管理员才能回复留言。

objContent->load_content_byPage()函数的代码如下:

function load_content_byPage($pageNo, $pageSize)

{

$sql = "SELECT * FROM Content WHERE UpperId=0 ORDER BY CreateTime DESC LIMIT ". ($pageNo-1) * $pageSize . "," . $pageSize;

$result = $this->conn->query($sql);

Return $result;

}

程序在SELECT语句中使用LIMIT关键字指定查询的范围。关于分页显示的具体实现方法可以参照 10.1.3 小节理解。objContent->load_content_byPage()函数值加载非回复的留言(即UpperId=0),回复留言在显示留言时通过调用$content->load_content_byUpperid()函数加载后显示。

10.2.5 添加新留言

在首页中单击“我要留言”超级链接,可以打开“添加新留言”窗口,如图10-7所示。

“我要留言”超级链接的定义代码如下:

<a target="_blank" href="newRec.php" onclick="return newwin(this.href)">我要留言</a>

在index.PHP中,定义了JavaScript函数newwin(),用于定义新窗口的模式,代码如下:

<script language="JavaScript">

function newwin(url) {

var

newwin=window.open(url,"newwin","toolbar=no,location=no,directories=no,status=no,menub ar=no,scrollbars=yes,resizable=yes,width=400,height=380");

newwin.focus();

return false;

}

</script>

figure_0197_0275

图10-7 “添加新留言”窗口

从“我要留言”超级链接的定义中可以看到,添加留言的脚本是newRec.php。在newRec.php中,使用表单formadd接受用户留言,定义代码如下:

<form method="POST" action="recSave.php?UpperId=<%=UpperId%>" name="formadd"onsubmit = "return ChkFields()">

当用户单击“提交”按钮时,将首先调用 ChkFields()方法进行有效性检查,然后执行recSave.php?UpperId=<%=UpperId%>存储信息。UpperId 表明当前留言是否是回复留言,如果UpperId=0,则表示当前留言为新留言,否则UpperId为回复留言的编号。

在“添加新留言”窗口中,用户可以使用下拉列表框选择自己的头像,代码如下:

<select size="1" name="logo" onChange="showlogo()">

<option selected value="1">1</option>

<option value="2">2</option>

<option value="3">3</option>

<option value="4">4</option>

<option value="5">5</option>

<option value="6">6</option>

<option value="7">7</option>

<option value="8">8</option>

<option value="9">9</option>

<option value="10">10</option>

<option value="11">11</option>

<option value="12">12</option>

<option value="13">13</option>

<option value="14">14</option>

<option value="15">15</option>

</select>&nbsp;&nbsp; <img src="images/1.gif" name="img">

下拉列表框的名称为logo,当用户选择下拉列表框的内容时,触发JavaScript函数showlogo(),改变用户头像。代码如下:

<script language="javascript">

function showlogo(){

document.images.img.src = "images/" + document.formadd.logo.options[document. formadd.logo.selectedIndex].value + ".gif";

}

</script>

document是JavaScript对象,表示当前的页面。Document.images.img表示当前页面中名为img的图片组件,src表示图片的源地址。

document.myform.logo表示当前页面中表单myform的logo组件(下拉框),options表示logo的选项,selectedIndex表示logo的当前被选索引,value表示下拉框的值。

onChange事件也可以应用于其他组件,如文本框。当用户输入数据时,可以通过程序控制执行相应的操作,如执行某种运算并显示结果,这样可以使网页的功能更强大。

recSave.php的主要代码如下:

<html>

<head>

<title>保存留言信息</title>

</head>

<body>

<?PHP

date_default_timezone_set('Asia/Chongqing'); //系统时间差8小时问题

include('Class\Content.php');

$objContent = new Content();

//从参数或表单中接收数据到变量中

$objContent->UserName = $_POST["name"];

$objContent->Subject = $_POST["Subject"];

$objContent->Words = $_POST["Words"];

$objContent->Email = $_POST["email"];

$objContent->Homepage = $_POST["homepage"];

$objContent->Face = $_POST["logo"];

$objContent->UpperId = $_POST["UpperId"];

if($objContent->UpperId == "")

$objContent->UpperId = 0;

//获取当前当前时间

$now = getdate();

$objContent->CreateTime = $now['year'] . "-" . $now['mon'] . "-" . $now['mday']

. " " . $now['hours'] . ":" . $now['minutes'] . ":" . $now['seconds'];

$objContent->insert();

echo("<h2>信息已成功保存!</h2>");

?>

</body>

<Script language="javascript">

//打开此脚本的网页将被刷新

opener.location.reload();

//停留800毫秒后关闭窗口

setTimeout("window.close()",2800);

</Script>

</html>

程序的运行步骤如下。

(1)定义Content类对象,用于操作表Content。

(2)读取表单域到Content对象对应的成员变量中。

(3)调用$objContent->insert()函数,保存留言记录。

(4)执行JavaScript脚本,刷新打开此窗口的网页(即index.php),然后关闭此窗口。具体说明如下:

• opener是JavaScript对象,表示打开当前页面的页面;location.reload()函数用于刷新页面;

• setTimeout()是JavaScript函数,语法如下:

setTimeout (表达式,延时时间)

• setTimeout()函数的功能是等候延时时间后,执行表达式;

• window.close()函数指定关闭当前窗口。

10.2.6 回复和删除留言

如果当前用户是管理员,则在主页中显示回复和删除超级链接,如图10-8所示。

回复和删除超级链接在Show.php中定义,其中“回复”超级链接的定义代码如下:

<a href=newRec.php?UpperId=<?PHP echo($row[0]); /*ContId*/ ?> target=_blank onclick="return newwin(this.href)">回复</a>

填写回复留言的脚本也是newRec.php,UpperId为当前留言的记录编号。回复留言和添加留言的方法完全相同,读者可以参照10.2.5小节理解。

“删除”超级链接的定义代码如下:

<a href=deleteRec.php?ContId=<?PHP echo($row[0]); /*ContId*/ ?> target=_blank onclick="return newwin(this.href)">删除</a>

figure_0199_0276

图10-8 管理员拥有回复和删除留言的权限

删除留言的脚本为deleteRec.php,主要代码如下:

<?PHP

include('ChkPwd.php');

include('Class\Content.php');

$ContId = $_GET["ContId"];  // 获取要删除的留言记录编号

$objContent = new Content();

$objContent->delete($ContId);  // 删除留言记录

echo("已成功删除留言。");

?>

<Script Language="JavaScript">

//打开此脚本的网页将被刷新

opener.location.reload();

//停留800ms后关闭窗口

setTimeout("window.close()",800);

</Script>

这段程序的功能比较简单,即使用参数ContId调用$objContent->delete()函数删除指定的留言记录和回复记录。$objContent->delete()函数的代码如下:

//删除指定的留言记录

function delete($Id)

{

$sql = "DELETE FROM Content WHERE ContId=" . $Id . " OR UpperId=" . $Id;

$this->conn->query($sql);

}

教程类别