10.3 设计“网络投票系统”实例
投票系统也是比较流行的一种常用Web应用系统,通常可以用来统计网友对网站设计或时事新闻的态度。本节介绍一个简单的网络投票系统的设计过程。
10.3.1 系统功能分析及数据库设计
网络投票系统模块的关系如图10-9所示。“系统管理员”用户Admin可以创建、修改和删除投票项目;普通用户则只能修改自己的用户名和密码。用户只有登录后,才能够实现管理投票项目的功能。由于篇幅所限,本实例不设计用户管理模块,读者可以参照10.2小节中的用户管理和登录模块理解。
图10-9 网络投票系统的模块关系图
在设计数据库表结构之前,首先要创建一个数据库。本系统使用的数据库为Vote。在数据库Vote中创建两个表,即表VoteItem和表VoteIP。
表VoteItem用来保存需要投票的项目信息,结构如表10-7所示。
表10-7 表VoteItem的结构
表VoteIP用来保存已经投票的IP地址,结构如表10-8所示。
表10-8 表VoteIP的结构
创建数据库的脚本如下:
CREATE DATABASE IF NOT EXISTS Vote
COLLATE 'gb2312_chinese_ci';
USE Vote;
CREATE TABLE IF NOT EXISTS Users (
Id INT AUTO_INCREMENT PRIMARY KEY,
Item VARCHAR(50),
VoteCount INT
);
CREATE TABLE IF NOT EXISTS VoteIP (
IP VARCHAR(50) PRIMARY KEY
);
此段脚本保存为下载源代码的10\Vote\vote.sql。
10.3.2 设计投票项目管理模块
系统管理员可以添加、修改和删除投票项目。设计投票项目管理的文件为 AddItem.PHP,界面如图10-10所示。
图10-10 投票项目管理界面
下面对AddItem.PHP中的代码进行分析。
1.显示项目
从数据库表VoteItem中提取并显示投票项目信息的代码如下:
<table border="1" cellspacing="0" width="90%" bordercolorlight="#4DA6FF"bordercolordark="#ECF5FF" style="FONT-SIZE: 9pt">
<tr>
<td width="60%" align="center" bgcolor="#FEEC85"><strong>项目</strong></td>
<td width="20%" align="center" bgcolor="#FEEC85"><strong>修改</strong></td>
<td width="20%" align="center" bgcolor="#FEEC85"><strong>选择</strong></td>
</tr>
<?PHP
$hasData = false; // 记录$results中是否存在记录
$results = $obj->load_VoteItem();
while($row = $results->fetch_row()) {
$hasData = true;
?>
<tr><td> <?PHP echo($row[1]);?> </td>
<td align="center"><a href="AddItem.php?Oper=update&id=<?PHPecho($row[0]);?>&name=<?PHP echo($row[1]); ?>">修改</a></td>
<td align="center"><input type="checkbox" name="voteitem" id=<?PHP echo($row[0]); ?>></td> </tr>
<?PHP
}
if(!$hasData) {
?>
<tr><td colspan=3 align=center><font style="COLOR:Red">目前还没有投票项目。</font></td></tr></table>
<?PHP } ?>
</table>
程序调用$obj->load_VoteItem()函数获取所有项目,结果集保存在$results中。然后使用while循环语句将$results中的记录输出到表格中。
2.读取参数值
url中参数oper表示要执行的操作,参数id表示操作的项目编号。获取url中参数的代码如下:
$Soperate = $_GET["Oper"]; //操作标记
$Operid = $_GET["id"]; //项目编号
参数oper表示的操作如表10-9所示。
表10-9 参数oper表示的操作
请结合下面的代码理解这些参数的使用方法。
if($Soperate=="add") { // 添加项目
$newTitle = $_POST["txttitle"];
echo($newTitle);
//判断数据库中是否存在此类别
if($obj->exists($newTitle))
echo("已经存在此投票项目,添加失败!");
else {
$obj->Item = $newTitle;
$obj->insert();
echo("投票项目已经成功添加!");
}
}
elseif($Soperate == "edit") { //修改项目
$newTitle = $_POST["txttitle"];
$orgTitle = $_POST["sOrgTitle"];
echo("newTitle : " . $newTitle . " orgTitle: " . $orgTitle);
//如果新类别名称和旧的不同则执行
if($newTitle<>$orgTitle) {
//判断数据库中是否存在此类别
if($obj->exists($newTitle))
echo("已经存在此投票项目,添加失败!");
else {
$obj->updateItem($newTitle, $Operid);
echo("投票项目已经成功修改!");
}
}
}
elseif($Soperate=="delete") { // 删除项目
$obj->delete($Operid);
echo("投票项目已经成功删除!");
}
3.添加项目
单击“添加”按钮,可以添加新的投票项目,并且提交表单。表单提交代码如下:
<form name="AForm" method="post" action="AddItem.PHP?Oper=add">
可以看到,提交数据后的处理脚本还是AddItem.PHP,参数Oper等于add表示添加数据,对应的处理代码如下:
if($Soperate=="add") { // 添加项目
$newTitle = $_POST["txttitle"];
echo($newTitle);
//判断数据库中是否存在此类别
if($obj->exists($newTitle))
echo("已经存在此投票项目,添加失败!");
else {
$obj->Item = $newTitle;
$obj->insert();
echo("投票项目已经成功添加!");
}
}
4.修改项目
单击“修改”按钮,可以修改指定的投票项目,并且提交表单。表单提交代码如下:
<form name="UFrom" method="post" action="AddItem.php?id=<?PHP echo($Operid); ?>&Oper=edit">
可以看到,提交数据后的处理脚本还是AddItem.PHP,参数Oper等于edit表示修改数据,对应的处理代码如下:
if($Soperate=="add") { // 添加项目
……
}
elseif($Soperate == "edit") { //修改项目
$newTitle = $_POST["txttitle"];
$orgTitle = $_POST["sOrgTitle"];
echo("newTitle : " . $newTitle . " orgTitle: " . $orgTitle);
//如果新类别名称和旧的不同则执行
if($newTitle<>$orgTitle) {
//判断数据库中是否存在此类别
if($obj->exists($newTitle))
echo("已经存在此投票项目,添加失败!");
else {
$obj->updateItem($newTitle, $Operid);
echo("投票项目已经成功修改!");
}
}
}
5.删除项目
按钮“全选”和按钮“清空”代码如下:
<input type="button" value="全选" onclick="sltAll()">
<input type="button" value="清空" onclick="sltNull()">
JavaScript函数sltAll()和sltNull()代码如下:
//全部选择
function sltAll() {
var nn = self.document.all.item("voteitem");
for(j=0;j<nn.length;j++) {
self.document.all.item("voteitem",j).checked = true;
}
}
//全部清空
function sltNull() {
var nn = self.document.all.item("voteitem");
for(j=0;j<nn.length;j++) {
self.document.all.item("voteitem",j).checked = false;
}
}
删除项目是通过选中复选框,单击“删除”按钮完成的。代码如下:
<input type="submit" value="删除" name="tijiao" onclick="SelectChk()">
SelectChk()是JavaScript函数,功能是得到要删除的项目的编号,然后删除项目。代码如下:
//选择要删除的项目后,提交表单
function SelectChk(){
var s=false;
var voteitemid,n=0;
var strid,strurl;
var nn = self.document.all.item("voteitem");
for (j=0;j<nn.length-1;j++) {
if (self.document.all.item("voteitem",j).checked){
n = n + 1;
s=true;
voteitemid = self.document.all.item("voteitem",j).id+"";
if(n==1)
strid = voteitemid;
else
strid = strid + "," + voteitemid;
}
}
strurl = "AddItem.PHP?Oper=delete&id=" + strid;
if(!s) {
alert("请选择要删除的投票项目!");
return false;
}
if ( confirm("你确定要删除这些投票项目吗?")) {
form1.action = strurl;
form1.submit();
}
}
可以看到,提交数据后的处理脚本还是AddItem.PHP,参数Oper等于delete表示删除数据,对应的处理代码如下:
if($Soperate=="add") { // 添加项目
……
}
elseif($Soperate == "edit") { // 修改项目
……
}
elseif($Soperate=="delete") { // 删除项目
$obj->delete($Operid);
echo("投票项目已经成功删除!");
}
6.重新投票
单击“重新投票”按钮,程序将清空投票数据。“重新投票”按钮的定义代码如下:
<input type="button" name="revote" value=" 重新投票 " onclick="returnnewwin('ReVote.php')">
重新投票的页面为ReVote.PHP。重新投票就是把所有的投票项目的投票数量改为0,而且删除所有投过票的IP地址。代码如下:
<?PHP
include('Class\VoteItem.php');
$objItem = new VoteItem();
$objItem->clearCount(); // 清空计数
include('Class\VoteIP.php');
$objIP = new VoteIP();
$objIP->deleteAll(); // 删除投票的IP
echo("系统重新投票!");
?>
$objItem.ClearCount()函数的代码如下:
function clearCount() {
$sql = "UPDATE VoteItem Set VoteCount=0 WHERE Id>0";
$this->conn->query($sql);
}
函数中执行UPDATE语句,将VoteCount字段的值设置为0。
$objIP->deleteAll()函数的代码如下:
//删除所有的投票IP
function deleteAll() {
$sql = "DELETE FROM VoteIP";
$this->conn->query($sql);
}
函数中执行DELETE语句,删除VoteIP表中的所有数据。
10.3.3 投票界面设计
投票界面为index.php文件,如图10-11所示。
图10-11 index.php的运行页面
首先判断是否存在投票项目,如果存在就加载项目。代码如下:
<?PHP
$isvoted = 0;
include('Class\VoteIP.php');
include('Class\VoteItem.php');
$objItem = new VoteItem();
$ItemCount = $objItem->getItemCount();
if($ItemCount == 0)
echo("现在没有投票项目");
else {
$results = $objItem->load_VoteItem();
……
?>
程序调用$objItem->getItemCount()函数返回表VoteItem中的项目数量。如果项目数量等于0,则输出“现在没有投票项目”,否则执行下面的内容。
程序会判断当前IP地址是否已经投过票,如果没有投过票,则使用while语句显示所有项目和用于投票的复选框。代码如下:
<?PHP
//取得当前投票人的ip地址,判断是否已经投票完毕
$ip = $_SERVER["REMOTE_ADDR"];
$objIP = new VoteIP();
if($objIP->exists($ip))
$isvoted = 1;
while($row = $results->fetch_row()) {
?>
<tr><td bgcolor="#FFFFFF">
<?PHP if($isvoted==0) { // 如果没有投过票,则显示复选框 ?>
<input type="checkbox" name="poster" id="<?PHP echo($row[0]); ?>">
<?PHP
} // end of if
echo($row[1]); ?></td>
</tr>
<?PHP
} // end of while
?>
如果已经投票(变量$isvoted为1),则显示“查看投票结果”超链接;否则显示投票按钮。代码如下:
<?PHP
//判断是否已经投票完毕
if($isvoted==1) { ?>
您已经投过票了 <a href=default.php onclick="return newwin(this. href)"><font color=blue>查看投票结果</font></a>
<?php }
else {
?> <input class=submit type=submit value="投票" name=submit onclick="return SelectChk();">
<?php }
?>
这是一个可以选择多项的投票系统,在 JavaScript函数 SelectChk()中取得被投票项目信息并提交。代码如下:
//取得被投票项目的编号,打开新窗口,查看投票结果
function SelectChk(){
var s=false;
var voteitemid,n=0;
var strid,strurl;
var nn = self.document.all.item("poster");
var j;
for (j=0;j<nn.length;j++) {
if (self.document.all.item("poster",j).checked) {
n = n + 1;
s=true;
voteitemid = self.document.all.item("poster",j).id+"";
if(n==1)
strid = voteitemid;
else
strid = strid + "," + voteitemid;
}
}
strurl = "postvote.php?cid=" + strid;
if(!s) {
alert("请选择投票项目!");
return false;
}
window.open(strurl,"newwin","toolbar=no,location=no,directories=no,status=no,menubar=n o,scrollbars=yes, resizable=yes,width=400,height=300");
return false;
}
投票提交给postvote.php文件,postvote.php会重新取得投票人的IP地址,并判断表VoteIP中是否存在此IP地址,如果没有,则插入此IP地址,并给被投票的项目数值分别加1。代码如下:
<?PHP
$ip = $_SERVER["REMOTE_ADDR"]; //取得投票人的IP地址
include('Class\VoteIP.php');
$objIP = new VoteIP();
//如果表中没有投过票,则插入记录
if($objIP->exists($ip)) {
echo("你已经投过票了,不得重复投票!");
}
else {
$objIP->IP = $ip;
$objIP->insert();
//投票项目数量加1
$ids = $_GET["cid"];
include('Class\VoteItem.php');
$objItem = new VoteItem();
$objItem->updateCount($ids);
echo("已成功投票");
}
?>
$objIP->exists()函数用于判断指定IP地址是否已经参与投票,代码如下:
//判断指定IP是否存在
function exists($_ip)
{
$result = $this->conn->query("SELECT * FROM VoteIP WHERE IP='" . $_ip . "'");
if($row = $result->fetch_row())
return true;
else
return false;
}
$objItem-> updateCount()函数用于将指定编号的项目投票数量加1,代码如下:
function updateCount($Ids) {
$sql = "UPDATE VoteItem SET VoteCount=VoteCount+1 WHERE Id IN (" . $Ids . ")";
$this->conn->query($sql);
}
投过票后再回到index.php,看到的页面如图10-12所示。
图10-12 投票后的index.PHP界面
投票后的可以查看投票结果,链接的代码如下:
<a href=default.php onclick=""return newwin(this.href)"">查看投票结果</a>
newwin()是一个打开新窗口的函数,就是打开default.PHP页面。代码如下:
function newwin(url) {
var oth="toolbar=no,location=no,directories=no,status=no,menubar=no,"
oth = oth + "scrollbars=yes,resizable=yes,left=200,top=200";
oth = oth + ",width=400,height=300";
var newwin=window.open(url,"newwin",oth);
newwin.focus();
return false;
}
default.PHP页面用于显示投票结果,可以查看投票结果和投票比率,如图10-13所示。
图10-13 查看投票结果
投票数百分比的计算代码如下:
<?PHP
//取得这批投票总数
include('Class\VoteItem.php');
$objItem = new VoteItem();
$total = $objItem->SumItemCount();
//取得每个投票项目信息
$results =$objItem->load_VoteItem();
while($row =$results->fetch_row()) {
if($total == 0)
$itotal = 1;
else
$itotal = $total;
//计算每个投票项目百分比图片长度
$imgvote = (int)$row[2] * 170 / $itotal;
?>
<tr><td bgcolor="#FFFFFF"><?PHP echo($row[1]);?></td>
<td colspan="2" bgcolor="#FFFFFF">
<img src=images/bar1.gif width=<?PHP echo($imgvote); ?> height=10><font style="font:7pt" face="Verdana">
<?PHP echo((int)$row[2]*100/$itotal); ?>%</font></td>
<td bgcolor="#FFFFFF" align="center"><?PHP echo($row[2]); ?></td>
</tr>
<?PHP } // end of while ?>
<tr> <td colspan="2" align="left"></td>
<td colspan="2" align="right">总票数:<?PHP echo($total); ?></td>
程序中调用$objItem->SumItemCount()函数计算投票项目的总数量,SumItemCount()函数的代码如下:
function SumItemCount() {
$sql = "SELECT Sum(VoteCount) FROM VoteItem";
$results = $this->conn->query($sql);
if($row = $results->fetch_row())
Return (int)$row[0];
else
Return 0;
}
变量$imgvote用于标识进度图片的宽度,其计算公式如下:
$imgvote = (int)$row[2] * 170 / $itotal;
这里指定图片的总长度为170,$row[2]是表VoteItem的第3个元素(第1个元素为$row[0]),即VoteCount的值,变量$itotal表示记录得到的总投票数量。