|
1.1 系 统 分 析
本节通过系统背景、系统模块分析和系统流程图3个方面来对系统进行分析。
1.1.1 系统背景
从国际互联网到校园网、企业局域网,各种网上投票系统随处可见。意见调查、用户信息统计、经营情况调查都可以作为投票的内容。网上投票系统凭借其方便快捷等特点,已经成为互联网资源中不可缺少的一部分。
网上投票系统是网站搜集用户需求并有效地实施市场策略的重要手段之一。通过开展问卷调查,可以迅速了解不同行业、不同区域用户的需求,客观地搜集需求信息,及时调整网站的营销策略以满足不同的需求。随着网络技术的发展,网上投票系统的作用将会越来越大。
1.1.2 系统模块分析
为了更合理地设计投票系统,需要从以下3点进行分析。
1.投票的形式
网上投票系统是网站搜集用户需求信息的一个途径,可以根据网站的需要设置一个或多个调查。不同的调查需要设置不同的选项,不同的调查要求,选项的形式也是不同的(单选或多选),不同时间段又会有不同的调查。既然网站要通过投票系统搜集信息,那么投票就必须有结果和对用户信息的统计。
2.投票的特点
由于互联网本身的开放性,使网上投票面临种种危险,也由此提出了相应的安全控制要求。
q 信息保密性:投票者有保密的要求。如果用户名及投票内容被人知悉,就对用户的隐私权构成了侵害。因此在网上投票系统中一般均有匿名投票的要求。
q 投票唯一性:一个投票者,其投票次数应当只有一次。若投票者可进行多次投票,这将对调查内容的可靠性构成严重的威胁。
有了这些特殊性的要求,就需要对用户的信息和投票进行检查和处理,以保证投票的客观和有效性。针对用户信息,如果无特殊的要求(例如只允许注册会员参加),那么系统就只需要记录的IP、投票时间、用户所在区域。投票结果的显示是非常重要的,一是用户希望自己的投票能够及时反映出来;二是投票的结果也是给希望投票的用户的一个参考。
3.投票系统结构
根据投票系统对形式的需求和投票系统自身的特点,需要包含以下几个模块。
q 投票管理模块:该模块可以添加、编辑和删除调查选项,设置调查选项为多选或单选,设置调查的时间期限,设置此调查是否启用,调查结果的统计显示。
q 调查显示模块:该模块的功能是显示已启用、未过期的调查。
q 投票处理模块:该模块需要对投票进行有效性检查,并将投票结果和用户信息写入数据库。
q 调查结果显示模块:该模块主要是计算每个调查选项统计结果的百分比并以图表的形式显示 出来。
q 数据库操作的基础模块:该模块定义了连接数据库、表的查询,数据的插入、更新和删除操作。该模块作为一个通用模块将会在后面的章节用到。
1.1.3 系统流程图
根据以上分析,系统的流程如图1.1所示。在该流程中,网站管理员在投票管理里面添加调查数据,然后在调查显示模块里面显示出来。用户通过调查显示的“投票”按钮投票,数据被传递到投票处理模块,处理之后转到调查结果显示页面。用户也可以通过调查显示的“查看结果”按钮直接转到调查结果显示页面。
图1.1 系统流程图
1.2 数据库建立
通过1.1节对投票系统功能的分析可知,需要存储的信息有调查信息、调查选项信息和用户信息。因此,本系统需要建立调查信息表、调查选项信息表和用户信息表。先从图1.2来分析这几个表之间的 关系。
从图1.2可以看出调查信息、调查选项信息和用户信息都是1对多的关系,并通过调查信息ID关联。构架的投票系统数据库vote(采用MySQL数据库)如表1.1~表1.3所示。
图1.2 数据表关系图
表1.1 调查信息表:EM_VOTE_INFO用于存储调查内容
字 段 名
| 类型(长度)
| 描 述
| 主 键
| 是否为空
| 默认值
| 备 注
| F_ID
| INT(10)
| 表ID(唯一)
| 是
| 否
| | 自动增加
| F_VOTE_TITLE
| VARCHAR(255)
| 调查标题
| 否
| 否
| | | F_VOTE_START
| INT(10)TIMESTAMP
| 调查开始时间
| 否
| 是
| | | F_VOTE_END
| INT(10)TIMESTAMP
| 调查结束时间
| 否
| 是
| | | F_VOTE_ITEM_TYPE
| TINYINT
| 调查选项类型
| 否
| 否
| 1
| 1为单选(默认)
2为多选
| F_VOTE_IS_DISPLAY
| TINYINT
| 是否启用
| 否
| 否
| 1
| 1为启用(默认)
0为禁用
| 表1.2 调查选项信息表:EE_ITEM_INFO用于存储调查的选项信息
字 段 名
| 类型(长度)
| 描 述
| 主 键
| 是否为空
| 默认值
| 备 注
| F_ID
| INT(10)
| 表ID(唯一)
| 是
| 否
| | 自动增加
| F_ID_VOTE_INFO
| INT(10)
| 调查表ID
| 否
| 否
| | 与调查表关联
| F_ITEM_TITLE
| VARCHAR(50)
| 选项标题
| 否
| 否
| | | F_ITEM_COUNT
| INT(10)
| 统计数量
| 否
| 否
| 0
| | F_ITEM_ORDER
| TINYINT
| 选项排列顺序
| 否
| 否
| 0
| 1为单选(默认)
| 表1.3 调查用户信息表:EE_VOTE_USER用于存储用户信息
字 段 名
| 类型(长度)
| 描 述
| 主 键
| 是否为空
| 默认值
| 备 注
| F_ID
| INT(10)
| 表ID(唯一)
| 是
| 否
| | 自动增加
| F_ID_VOTE_INFO
| INT(10)
| 调查表ID
| 否
| 否
| | 与调查表关联
| F_USER_IP
| INT(15)
| 用户IP
| 否
| 否
| | | 续表
字 段 名
| 类型(长度)
| 描 述
| 主 键
| 是否为空
| 默认值
| 备 注
| F_USER_TIME
| INT(10)TIMESTAMP
| 统计时间
| 否
| 否
| | | F_USER_AREA
| VARCHAR(20)
| 用户所在区域
| 否
| 否
| | | 1.3 数据库操作基础模块
数据操作基础模块主要实现数据库连接以及对数据库表的一些基本操作功能。它包括配置文件和数据库操作文件。下面分别对它们做详细讲解。
1.3.1 配置文件config.inc.php
建立配置文件是系统构架需要考虑的重点。因为在一个系统里会有一些常用的参数在很多地方可以用到,如果到使用的时候才定义,那么需要修改的时候就会相当的麻烦,而且容易出错,代码的可读性也很差。在配置文件里放的主要是数据库连接用的参数和一些全局变量。代码如下:
<?php
define("UserName", "root"); //数据库连接用户名
define("PassWord", "root"); //数据库连接密码
define("ServerName", "localhost"); //数据库服务器的名称
define("DBName","Languagevote"); //数据库名称
define("ERRFILE","err.php"); //错误处理显示文件
define('ROOT_PATH', dirname(_FILE_) . '/'); //定义根目录路径
define('INCLUDE_PATH', ROOT_PATH . 'include/'); //定义包含文件目录路径
?>
1.3.2 数据操作文件db.inc.php
PHP是一种面向对象的编程语言。这个数据操作文件主要用于建立一个类。该类的初始化构造函数可以连接数据库和表。其他的方法包括对表的查询、数据的插入、更新、删除操作和事务处理。事务处理是用在执行多个更新或删除操作时为了保证数据完整性而使用的。把这些基本操作封装在一个模块里面对于代码的可读性、系统的扩展性和健壮性都有好处。代码如下:
<?php
/**
功能:数据库的基础操作类
**/
class DBSQL{
private $CONN = ""; //定义数据库连接变量
/**
* 功能:初始化构造函数,连接数据库
*/
public function _construct(){
try { //捕获连接错误并显示错误文件
$conn = mysql_connect(ServerName,UserName,PassWord);
}catch (Exception $e)
{
$msg = $e;
include(ERRFILE);
}
try { //捕获数据库选择错误并显示错误文件
mysql_select_db(DBName,$conn);
}catch (Exception $e)
{
$msg = $e;
include(ERRFILE);
}
$this->CONN = $conn;
}
/**
* 功能:数据库查询函数
* 参数:$sql SQL语句
* 返回:二维数组或false
*/
public function select($sql = ""){
if (empty($sql)) return false; //如果SQL语句为空则返回false
if (empty($this->CONN)) return false; //如果连接为空则返回false
try{ //捕获数据库选择错误并显示错误文件
$results = mysql_query($sql,$this->CONN);
}catch (Exception $e){
$msg = $e;
include(ERRFILE);
}
if ((!$results) or (empty($results))) { //如果查询结果为空则释放结果并返回false
@mysql_free_result($results);
return false;
}
$count = 0;
$data = array();
while ($row = @mysql_fetch_array($results)) { //把查询结果重组成一个二维数组
$data[$count] = $row;
$count++;
}
@mysql_free_result($results);
return $data;
}
/**
* 功能:数据插入函数
* 参数:$sql SQL语句
* 返回:0或新插入数据的ID
*/
public function insert($sql = ""){
if (empty($sql)) return 0; //如果SQL语句为空则返回false
if (empty($this->CONN)) return 0; //如果连接为空则返回false
try{ //捕获数据库选择错误并显示错误文件
$results = mysql_query($sql,$this->CONN);
}catch(Exception $e){
$msg = $e;
include(ERRFILE);
}
if (!$results) //如果插入失败就返回0,否则返回当前插入数据ID
return 0;
else
return @mysql_insert_id($this->CONN);
}
/**
* 功能:数据更新函数
* 参数:$sql SQL语句
* 返回:TRUE OR FALSE
*/
public function update($sql = ""){
if(empty($sql)) return false; //如果SQL语句为空则返回false
if(empty($this->CONN)) return false; //如果连接为空则返回false
try{ //捕获数据库选择错误并显示错误文件
$result = mysql_query($sql,$this->CONN);
}catch(Exception $e){
$msg = $e;
include(ERRFILE);
}
return $result;
}
/**
* 功能:数据删除函数
* 参数:$sql SQL语句
* 返回:TRUE OR FALSE
*/
public function delete($sql = ""){
if(empty($sql)) return false; //如果SQL语句为空则返回false
if(empty($this->CONN)) return false; //如果连接为空则返回false
try{
$result = mysql_query($sql,$this->CONN);
}catch(Exception $e){
$msg = $e;
include(ERRFILE);
}
return $result;
}
/**
* 功能:定义事务
*/
public function begintransaction()
{
mysql_query("SET AUTOCOMMIT=0"); //设置为不自动提交,因为MySQL默认立即执行
mysql_query("BEGIN"); //开始事务定义
}
/**
* 功能:回滚
*/
public function rollback()
{
mysql_query("ROOLBACK");
}
/**
* 功能:提交执行
*/
public function commit()
{
mysql_query("COMMIT");
}
}
?>
1.4 投票管理模块
投票管理模块用于实现网站管理员对投票数据的管理,可以添加、编辑、删除调查选项,还可以查看调查统计的信息。该模块包括调查类文件、调查列表文件、添加调查文件、编辑调查文件、删除调查文件以及用户统计文件。下面对这几个文件分别进行讲解。
1.4.1 调查类文件vote.inc.php
该文件是一个类文件。它的功能主要是对调查信息、表1.2和表1.3的操作,除一些基本操作外,在开发的过程中还可以根据需要添加其他的操作。调查类文件作为一个包含文件被调用。代码如下:
<?php
require_once(INCLUDE_PATH . 'db.inc.php');
class Vote extends DBSQL
{
public $_name = 'EM_VOTE_INFO'; //定义调查表名称变量
public $_item = 'EE_ITEM_INFO'; //定义调查选项表名称变量
public $_user = 'EE_VOTE_USER'; //定义用户信息表名称变量
public $_pagesize = 10; //定义每页提取记录数
public $_type = array("1"=>"单选", //定义选项类型
"2"=>"多选");
public $_display = array("0"=>"禁用", //定义调查启用显示
"1"=>"启用");
private function _construct()
{
parent::_construct();
}
/**
* 功能:提取调查列表
* 返回:数组
*/
public function getVoteList(){
$sql = "SELECT * FROM " . $this->_name;
return $this->select($sql);
}
/**
* 功能:提取指定表的指定ID的记录
* 参数:$id 表ID,$name 表名称
* 返回:数组
*/
public function getInfo($id,$name)
{
$sql = "SELECT * FROM " . $name . " WHERE F_ID = $id";
$r = $this->select($sql);
return $r[0];
}
/**
* 功能:向指定表中插入数据
* 参数:$name 表名称,$data 数组(格式:$data['字段名'] = 值)
* 返回:插入记录ID
*/
public function insertData($name,$data)
{
$field = implode(',',array_keys($data)); //定义SQL语句的字段部分
foreach($data as $key => $val) //组合SQL语句的值部分
{
$value .= "'" . $val . "'";
if($key < count($data) - 1) //判断是否到数组的最后一个值
$value .= ",";
}
$sql = "INSERT INTO " . $name . "(" . $field . ") VALUES(" . $value . ")";
return $this->insert($sql);
}
/**
* 功能:更新指定表指定ID的调查表记录
* 参数:$name 表名称,$id 表ID,$data 数组(格式:$data['字段名'] = 值)
* 返回:TRUE OR FALSE
*/
public function updateData($name,$id,$data){
$col = array();
foreach ($data as $key => $value)
{
$col[] = $key . "='" . $value . "'";
}
$sql = "UPDATE " . $name . " SET " . implode(',',$col) . " WHERE F_ID = $id";
return $this->update($sql);
}
/**
* 功能:删除指定ID的调查表记录及相关表记录
* 参数:$id 调查表ID
* 返回:TRUE OR FALSE
*/
public function delData($id){
$this->begintransaction();
try{
$sql = "DELETE FROM " . $this->_item . " WHERE F_ID_VOTE_INFO = " . $id;
$this->delete($sql); //删除调查选项里面的相关数据
$sql = "DELETE FROM " . $this->_user . " WHERE F_ID_VOTE_INFO = " . $id;
$this->delete($sql); //删除用户统计表里面的相关数据
$sql = "DELETE FROM " . $this->_name . " WHERE F_ID = " . $id;
$this->delete($sql);
}catch(Exception $e){
$this->rollback();
return false;
}
$this->commit();
return true;
}
/**
* 功能:提取指定调查ID的选项
* 参数:$vote_id 调查ID
* 返回:数组
*/
public function getItemList($vote_id)
{
$sql = "SELECT * FROM " . $this->_item . " WHERE F_ID_VOTE_INFO = $vote_id";
return $this->select($sql);
}
/**
* 功能:删除指定ID的选项表记录
* 参数:$id 表ID
* 返回:TRUE OR FALSE
*/
public function delItemData($id)
{
$sql = "DELETE FROM " . $this->_item . " WHERE F_ID = $id";
return $this->delete($sql);
$sql .= “ ORDER BY F_ITEM_ORDER”;}
/**
* 功能:提取指定调查ID的用户统计信息
* 参数:$vote_id 调查ID,$page 当前页码
* 返回:数组
*/
public function getUserList($vote_id,$page=1)
{
$start = ($page - 1) * $this->_pagesize;
$sql = "SELECT * FROM " . $this->_user . " WHERE F_ID_VOTE_INFO = $vote_id";
$sql .= " LIMIT $start,$this->_pagesize";
return $this->select($sql);
}
/**
* 功能:提取指定调查ID用户统计记录的条数
* 参数:$vote_id 调查ID
* 返回:记录条数
*/
public function getUserCount($vote_id)
{
$sql = "SELECT COUNT(F_ID) FROM " . $this->_user . " WHERE F_ID_VOTE_INFO = $vote_id";
$r = $this->select($sql);
return $r[0][0];
}
/**
* 功能:删除指定ID的用户统计记录
* 参数:$id 用户统计表ID
* 返回:TRUE OR FALSE
*/
public function delUserData($id)
{
$sql = "DELETE FROM " . $this->_user . " WHERE F_ID = $id";
return $this->delete($sql);
}
}
?>
对单个表进行查询、插入、更新和删除时的代码很相似,唯一不同的是操作表的名称。这里可以把这些对单个表的基本操作放到基础类文件db.inc.php里面。加入如下代码:
/**
* 功能:提取指定表的指定ID的记录
* 参数:$id 表ID,$name 表名称
* 返回:数组
*/
public function getInfo($id,$name)
{
$sql = "SELECT * FROM " . $name . " WHERE F_ID = $id";
$r = $this->select($sql);
return $r[0];
}
/**
* 功能:向指定表中插入数据
* 参数:$name 表名称,$data 数组(格式:$data['字段名'] = 值)
* 返回:插入记录ID
*/
public function insertData($name,$data)
{
$field = implode(',',array_keys($data)); //定义SQL语句的字段部分
$i = 0;
foreach($data as $key => $val) //组合SQL语句的值部分
{
$value .= "'" . $val . "'";
if($i < count($data) - 1) //判断是否到数组的最后一个值
$value .= ",";
$i++;
}
$sql = "INSERT INTO " . $name . "(" . $field . ") VALUES(" . $value . ")";
return $this->insert($sql);
}
/**
* 功能:更新指定表指定ID的调查表记录
* 参数:$name 表名称,$id 表ID,$data 数组(格式:$data['字段名'] = 值)
* 返回:TRUE OR FALSE
*/
public function updateData($name,$id,$data){
$col = array();
foreach ($data as $key => $value)
{
$col[] = $key . "='" . $value . "'";
}
$sql = "UPDATE " . $name . " SET " . implode(',',$col) . " WHERE F_ID = $id";
return $this->update($sql);
}
/**
* 功能:删除指定ID的表记录
* 参数:$id表ID,$name 表名称
* 返回:TRUE OR FALSE
*/
public function delData($id,$name)
{
$sql = "DELETE FROM " . $name . " WHERE F_ID = $id";
return $this->delete($sql);
}
1.4.2 调查列表文件VoteList.php
该文件的功能是显示调查信息表1.1中的数据列表。该文件包含配置文件调查类文件。提取列表的数据是通过调查类文件里面的提取列表方法来实现的。首先声明一个调查类Vote的对象,通过这个对象来调用类的提取列表方法getVoteList()。通过这个页面连接到添加、编辑、选项管理、用户统计及删除操作页面。界面如图1.3所示。
图1.3 调查列表
代码如下:
<?php
require_once("config.inc.php");
require_once(INCLUDE_PATH . 'vote.inc.php');
$vote = new Vote(); //声明一个对象$vote
$list = $vote->getVoteList();
$time = time();
?>
<form name="form1" action="" method="post">
<table width="98%" border="0" align="center" cellspacing="0" class="l_table_1" id="table_1">
<tr class="title">
<td width="5%">序号</td>
<td width="24%">调查标题</td>
<td width="14%">开始时间</td>
<td width="12%">结束时间</td>
<td width="12%">选项类型</td>
<td width="6%">是否过期</td>
<td width="6%">是否启用</td>
<td width="21%">操作</td>
</tr>
<?php
if($list) //如果有记录则循环显示
{
foreach($list as $key => $value)
{
?>
<tr class="l_field">
<td align="left"><?php echo ($key + 1)?></td>
<td align="left"><?php echo $value['F_VOTE_TITLE']?></td>
<td align="left"><?php echo date('Y-m-d',$value['F_VOTE_START'])?></td>
<td align="left"><?php echo date('Y-m-d',$value['F_VOTE_END'])?></td>
<td align="left"><?php echo $vote->_type[$value['F_VOTE_ITEM_TYPE']]?></td>
<td align="left"><?php if($value['F_VOTE_END'] > $time) echo "未过期";else echo "已过期";?></td>
<td align="left"><?php echo $vote->_display[$value['F_VOTE_IS_DISPLAY']]?></td>
<td align="left"><a href="EditVote.php?id=<?php echo $value['F_ID']?>">[编辑]</a> <a href= "ItemList.php?id=<?php echo $value['F_ID']?>">[选项管理]</a> <a href="UserList.php?id=<?php echo $value['F_ID']?>">[用户统计信息]</a> <a href="DelVote.php?id=<?php echo $value['F_ID']?>">[删除]</a> </td>
</tr>
<?php
}
}
?>
<tr>
<td colspan="13" align="center"><input type="submit" name="Submit3" value=" 添 加 调 查 " onclick= "javascript:window.location='AddVote.php'" /></td>
</tr>
</table>
</form>
1.4.3 添加调查文件AddVote.php
该文件的功能是添加新调查,将数据写入表1.1中。该文件由图1.3中调查列表页面的“添加调查”按钮连接过来。用户填写完表单后单击“提交”按钮进行处理。表单提交给自身,通过判断是否为提交操作进行数据处理。数据的处理通过调查类Vote的对象调用父类DBSQL的insertData()方法实现的,界面如图1.4所示。
图1.4 添加调查
1.主程序部分
该部分代码用于实现提交数据的处理和操作界面的显示。代码如下:
<?php
require_once('../config.inc.php');
require_once(INCLUDE_PATH . 'vote.inc.php');
$vote = new Vote();
list($year,$month,$day) = explode("-",date('Y-m-d'));
if($_SERVER['REQUEST_METHOD'] == 'POST') //判断是否是提交请求,若是则添加数据
{
$data['F_VOTE_TITLE'] = $_POST['title'];
$data['F_VOTE_START'] = mktime(0,0,0,$_POST['start_m'],$_POST['start_d'],$_POST['start_y']);
$data['F_VOTE_END'] = mktime(0,0,0,$_POST['end_m'],$_POST['end_d'],$_POST['end_y']);
$data['F_VOTE_ITEM_TYPE'] = $_POST['type'];
$data['F_VOTE_IS_DISPLAY'] = $_POST['display'];
if($vote->insertData($vote->_name,$data)) //判断是否操作成功
echo "操作成功";
else
echo "操作失败";
echo "<a href='VoteList.php'>返回</a>";
exit();
}
?>
<script language="javascript" src="../js/date.js"></script>
<form name="form1" action="" method="post" >
<table width="60%" border="0" align="center" cellpadding="0" cellspacing="0" class="l_table">
<tr class="title">
<td colspan="2" align="left"> </td>
</tr>
<tr class="l_field">
<td align="right">调查标题:</td>
<td><input name="title" type="text" id="title" size="40" /></td>
</tr>
<tr class="l_field">
<td align="right">所属类型:</td>
<td><select name="type" id="type">
<?php
foreach ($vote->_type as $key => $value) //循环显示类型选择下拉列表框
{
echo "<option value=$key>$value</option>";
}
?>
</select> </td>
</tr>
<tr class="l_field">
<td width="24%" align="right">开始时间:</td>
<td width="76%">
<select name="start_y" id="start_y">
<?php
for($i=1;$i<=($year+1);$i++) //循环显示开始年份下拉列表框
{
echo "<option value=$i";
if($i == $year) //设置默认选项
echo " selected='selected'";
echo ">$i</option>";
}
?>
</select>
年
<select name="start_m" id="start_m" onchange="javascript:register_buildDay(this.value);">
<?php
for($i=1;$i<=12;$i++) //循环显示开始月份下拉列表框
{
if($i < 10)
$i = '0' . $i;
echo "<option value=$i";
if($i == $month) //设置默认选项
echo " selected='selected'";
echo ">$i</option>";
}
?>
</select>
月
<select name="start_d" id="start_d">
<?php
echo "<option value='$day'>$day</option>"; //列表
?>
</select>
日 </td>
</tr>
<tr class="l_field">
<td align="right">结束时间:</td>
<td>
<select name="end_y" id="end_y">
<?php
for($i=1;$i<=($year+1);$i++) //循环显示结束年份下拉列表框
{
echo "<option value=$i";
if($i == $year) //设置默认选项
echo " selected='selected'";
echo ">$i</option>";
}
?>
</select>
年
<select name="end_m" id="end_m" onchange="javascript:register_buildDay(this.value);">
<?php
for($i=1;$i<=12;$i++) //循环显示结束月份下拉列表框
{
if($i < 10)
$i = '0' . $i;
echo "<option value=$i";
if($i == $month) //设置默认选项
echo " selected='selected'";
echo ">$i</option>";
}
?>
</select>
月
<select name="end_d" id="end_d">
<?php
echo "<option value='$day'>$day</option>";
?>
</select>
日 </td>
</tr>
<tr class="l_field">
<td align="right">是否启用:</td>
<td><input name="display" type="radio" value="1" checked="checked" />
启用
<input type="radio" name="display" value="0" />
禁用</td>
</tr>
<tr class="title">
<td colspan="2" align="center"><input type="submit" name="Submit" value="提交" />
<input type="reset" name="reset" value="重置" /></td>
</tr>
</table>
<table align="center" border="0" cellpadding="0" cellspacing="0" width="98%">
<tbody><tr>
<td> </td>
</tr>
</tbody></table>
</form>
2.客户端程序(JavaScript)
在主程序代码里,表单提交之前会用到客户端语言JavaScript来实现日期的选择和数据正确性、完整性检查。数据正确性、完整性检查是在处理添加和编辑数据页时必需的。必须检查那些在数据库里面不能为空的字段和有特殊格式的字段,保证它的正确性和完整性。调用JavaScript是通过表单中的onsubmit='javascript:return check();'实现的。这个调用的意思是:如果返回true,就提交表单;否则不提交。check()函数的代码如下:
<script language="javascript">
//功能:检查日期格式是否是有效格式
function checkIsValidDate(str)
{
if(str == "") //如果参数为空,则返回false
return false;
var arrDate = str.split("-"); //把参数用split函数分割成数组,它等同与PHP中的explode函数
if(parseInt(arrDate[0],10) < 100) //如果年份小于100,则表示是21世纪
arrDate[0] = 2000 + parseInt(arrDate[0],10) + "";
var date = new Date(arrDate[0],(parseInt(arrDate[1],10) -1)+"",arrDate[2]); //格式化为日期格式
if(date.getYear() == arrDate[0] //判断格式化后日期的年,月,日是否和参数的相等,相等则是有效格式
&& date.getMonth() == (parseInt(arrDate[1],10) -1)+""
&& date.getDate() == arrDate[2])
return true;
else
return false;
}
//功能:检查开始时间是否小于结束日期
//参数:strStart 开始日期,strEnd 结束日期
function checkDateEarlier(strStart,strEnd)
{
if(checkIsValidDate(strStart) == false || checkIsValidDate(strEnd) == false)
return false; //检查日期格式是否有效
if (( strStart == "" ) || ( strEnd == "" )) //检查日期是否为空
return false;
var arr1 = strStart.split("-");
var arr2 = strEnd.split("-");
var date1 = new Date(arr1[0],parseInt(arr1[1].replace(/^0/,""),10) - 1,arr1[2]);
var date2 = new Date(arr2[0],parseInt(arr2[1].replace(/^0/,""),10) - 1,arr2[2]);
if(arr1[1].length == 1) //将月份格式化为08这种形式
arr1[1] = "0" + arr1[1];
if(arr1[2].length == 1) //将日格式化为08这种形式
arr1[2] = "0" + arr1[2];
if(arr2[1].length == 1) //将月份格式化为08这种形式
arr2[1] = "0" + arr2[1];
if(arr2[2].length == 1) //将日格式化为08这种形式
arr2[2]="0" + arr2[2];
var d1 = arr1[0] + arr1[1] + arr1[2]; //将开始日期组合成一个字符串
var d2 = arr2[0] + arr2[1] + arr2[2]; //将结束日期组合成一个字符串
if(parseInt(d1,10) > parseInt(d2,10)) //将两个字符串转化成整数,如果d1>d2则开始日期大于结束日期
return false;
else
return true;
}
//定义一个javascript的原形
String.prototype.len = function(){ //计算字符串的长度(一个双字节字符长度计2,ASCII字符计1)
return this.replace(/[^\x00-\xff]/g,"aa").length;
}
function check()
{
var Start = document.form1.start_y.value + "-" + document.form1.start_m.value + "-" + document. form1.start_d.value;
var End = document.form1.end_y.value + "-" + document.form1.end_m.value + "-" + document. form1.end_d.value;
if(document.form1.title.value == '') //判断标题是否为空,为空则返回false
{
alert('请填写调查名称');
document.form1.title.focus();
return false;
}
if(document.form1.title.value.len() > 100) //判断标题长度是否超过100
{
alert('调查标题不能超过100个字')
document.form1.title.focus();
return false;
}
if(!checkDateEarlier(Start,End)) //判断开始日期是否大于结束日期
{
alert('开始日期因不能小于结束日期');
return false;
}
}
</script>
日期处理JavaScript文件date.js的代码如下:
//功能:显示开始日期指定月份的天数
//参数:month 月份
function register_buildDay(month)
{
var yearOb=document.getElementById('start_y'); //取得开始日期年份
var dayOb=document.getElementById('start_d'); //取得开始日期天数
document.getElementById('start_d').length = 0;
var lastDay=register_getDay(yearOb.value,Number(month)); //取得当月的天数
for(var i=1;i<=lastDay;i++) //循环输出下拉列表框
{
var dayOption=document.createElement("OPTION");
dayOb.options.add(dayOption);
dayOption.innerText=i;
dayOption.value=i;
dayOb.selectedIndex=0;
}
}
//功能:重新设置开始日期的天数
function register_resetDay()
{
var dayObject=document.getElementById('start_d');
var dayLength=dayObject.length;
for(var i=1;i<dayLength;dayLength--) //将开始日期天数的下拉列表框循环移处
{
dayObject.remove(i);
}
}
//功能:显示结束日期指定月份的天数
//参数:month 月份
function register_buildEndDay(month)
{
var yearOb=document.getElementById('end_y'); //取得结束日期的年份
var dayOb=document.getElementById('end_d'); //取得结束日期的天数
document.getElementById('end_d').length = 0;
var lastDay=register_getDay(yearOb.value,Number(month)); //取得当月的天数
for(var i=1;i<=lastDay;i++) //循环输出下拉列表框
{
var dayOption=document.createElement("OPTION");
dayOb.options.add(dayOption);
dayOption.innerText=i;
dayOption.value=i;
dayOb.selectedIndex=0;
}
}
//功能:重新设置结束日期天数
function register_resetEndDay()
{
var dayObject=document.getElementById('end_d');
var dayLength=dayObject.length;
for(var i=1;i<dayLength;dayLength--) //将结束日期天数的下拉列表框循环移处
{
dayObject.remove(i);
}
}
//功能:取得指定年份和月份的天数
//参数:year 年份 month 月份
function register_getDay(Year,Month)
{
var LastDay = 0;
switch (Month) //判断月份,1,3,5,7,8,10,12天数为31天,4,6,
9,11为30天
{
case 1:
case 3:
case 5:
case 7:
case 8:
Month="0"+ Month;
case 10:
case 12:
LastDay=31;
break;
case 4:
case 6:
case 9:
Month="0"+ Month;
case 11:
LastDay=30;
break;
case 2: //判断是否为闰年,是则2月为29天,不是则为28天
Month="0"+ Month;
if ((Year%4==0&&Year%100!=0)||Year%400==0)
{
LastDay=29;
}
else
{
LastDay=28;
}
break;
default:
LastDay=0;
}
return LastDay;
}
1.4.4 编辑调查文件EditVote.php
该文件的功能是编辑调查信息。该文件由图1.3中调查列表页面的操作列“[编辑]”连接过来传递调查ID参数。用户编辑表单数据后单击“提交”按钮进行数据处理。表单数据提交给自身,通过判断是否为提交操作来处理数据。处理数据通过调查类Vote的对象来调用父类DBSQL的updateData()方法实现的。其界面如图1.5所示。
图1.5 编辑调查
代码如下:
<?php
require_once('../config.inc.php');
require_once(INCLUDE_PATH . 'vote.inc.php');
$vote = new Vote();
$info = $vote->getInfo($_GET['id'],$vote->_name);
$year = date("Y");
list($start_year,$start_month,$start_day) = explode("-",date('Y-m-d',$info['F_VOTE_START']));
list($end_year,$end_month,$end_day) = explode("-",date('Y-m-d',$info['F_VOTE_END']));
if($_SERVER['REQUEST_METHOD'] == 'POST') //判断是否为提交请求
{
$data['F_VOTE_TITLE'] = $_POST['title'];
$data['F_VOTE_START'] = mktime(0,0,0,$_POST['start_m'],$_POST['start_d'],$_POST['start_y']);
$data['F_VOTE_END'] = mktime(0,0,0,$_POST['end_m'],$_POST['end_d'],$_POST['end_y']);
$data['F_VOTE_ITEM_TYPE'] = $_POST['type'];
$data['F_VOTE_IS_DISPLAY'] = $_POST['display'];
if($vote->updateData($vote->_name,$_POST['id'],$data)) //判断是否操作成功
echo "操作成功";
else
echo "操作失败";
echo "<a href='VoteList.php'>返回</a>";
exit(); }
}
?>
<form name="form1" action="" method="post" >
<table width="60%" border="0" align="center" cellpadding="0" cellspacing="0" class="l_table">
<tr class="title">
<td colspan="2" align="left"> </td>
</tr>
<tr class="l_field">
<td align="right">调查标题:</td>
<td><input name="title" type="text" id="title" size="40" value="<?php echo $info['F_VOTE_TITLE']?>" /></td>
</tr>
<tr class="l_field">
<td align="right">所属类型:</td>
<td><select name="type" id="type">
<?php
foreach ($vote->_type as $key => $value)
{
echo "<option value=$key";
if($info['F_VOTE_ITEM_TYPE'] == $key)
echo " selected='selected'";
echo ">$value</option>";
}
?>
</select> </td>
</tr>
<tr class="l_field">
<td width="24%" align="right">开始时间:</td>
<td width="76%">
<select name="start_y" id="start_y">
<?php
for($i=1;$i<=($year+1);$i++)
{
echo "<option value=$i";
if($i == $start_year)
echo " selected='selected'";
echo ">$i</option>";
}
?>
</select>
年
<select name="start_m" id="start_m" onchange="javascript:register_buildDay(this.value);">
<?php
for($i=1;$i<=12;$i++)
{
if($i < 10)
$i = '0' . $i;
echo "<option value=$i";
if($i == $start_month)
echo " selected='selected'";
echo ">$i</option>";
}
?>
</select>
月
<select name="start_d" id="start_d">
<?php
echo "<option value='$start_day'>$start_day</option>";
?>
</select>
日 </td>
</tr>
<tr class="l_field">
<td align="right">结束时间:</td>
<td>
<select name="end_y" id="end_y">
<?php
for($i=1;$i<=($year+1);$i++)
{
echo "<option value=$i";
if($i == $end_year)
echo " selected='selected'";
echo ">$i</option>";
}
?>
</select>
年
<select name="end_m" id="end_m" onchange="javascript:register_buildDay(this.value);">
<?php
for($i=1;$i<=12;$i++)
{
if($i < 10)
$i = '0' . $i;
echo "<option value=$i";
if($i == $end_month)
echo " selected='selected'";
echo ">$i</option>";
}
?>
</select>
月
<select name="end_d" id="end_d">
<?php
echo "<option value='$end_day'>$end_day</option>";
?>
</select>
日 </td>
</tr>
<tr class="l_field">
<td align="right">是否启用:</td>
<td><input name="display" type="radio" value="1"<?php if($info['F_VOTE_IS_DISPLAY'] == 1) echo "checked='checked'";?> />
启用
<input type="radio" name="display" value="0"<?php if($info['F_VOTE_IS_DISPLAY'] == 0) echo "checked='checked'";?> />
禁用</td>
</tr>
<tr class="title">
<td colspan="2" align="center"><input type="submit" name="Submit" value="提交" />
<input type="reset" name="reset" value="重置" />
<input name="id" type="hidden" id="id" value="<?php echo $_GET['id']?>" /></td>
</tr>
</table>
<table align="center" border="0" cellpadding="0" cellspacing="0" width="98%">
<tbody><tr>
<td> </td>
</tr>
</tbody></table>
</form>
<script language="javascript">
function checkIsValidDate(str)
{
if(str == "")
return true;
var arrDate = str.split("-");
if(parseInt(arrDate[0],10) < 100)
arrDate[0] = 2000 + parseInt(arrDate[0],10) + "";
var date = new Date(arrDate[0],(parseInt(arrDate[1],10) -1)+"",arrDate[2]);
if(date.getYear() == arrDate[0]
&& date.getMonth() == (parseInt(arrDate[1],10) -1)+""
&& date.getDate() == arrDate[2])
return true;
else
return false;
}
function checkDateEarlier(strStart,strEnd)
{
if(checkIsValidDate(strStart) == false || checkIsValidDate(strEnd) == false)
return false;
if (( strStart == "" ) || ( strEnd == "" ))
return true;
var arr1 = strStart.split("-");
var arr2 = strEnd.split("-");
var date1 = new Date(arr1[0],parseInt(arr1[1].replace(/^0/,""),10) - 1,arr1[2]);
var date2 = new Date(arr2[0],parseInt(arr2[1].replace(/^0/,""),10) - 1,arr2[2]);
if(arr1[1].length == 1)
arr1[1] = "0" + arr1[1];
if(arr1[2].length == 1)
arr1[2] = "0" + arr1[2];
if(arr2[1].length == 1)
arr2[1] = "0" + arr2[1];
if(arr2[2].length == 1)
arr2[2]="0" + arr2[2];
var d1 = arr1[0] + arr1[1] + arr1[2];
var d2 = arr2[0] + arr2[1] + arr2[2];
if(parseInt(d1,10) > parseInt(d2,10))
return false;
else
return true;
}
String.prototype.len = function(){
return this.replace(/[^\x00-\xff]/g,"aa").length;
}
function check()
{
var Start = document.form1.start_y.value + "-" + document.form1.start_m.value + "-" + document. form1. start_d.value;
var End = document.form1.end_y.value + "-" + document.form1.end_m.value + "-" + document. form1. end_d.value;
if(document.form1.title.value == '')
{
alert('请填写调查名称');
document.form1.title.focus();
return false;
}
if(document.form1.title.value.len() > 100)
{
alert('调查标题不能超过100个字')
document.form1.title.focus();
return false;
}
if(!checkDateEarlier(Start,End))
{
alert('开始日期因不能小于结束日期');
return false;
}
}
</script>
比较添加调查和编辑调查文件,可以看出它们的代码很相似,不同的是在编辑时多了提取编辑信息的内容并显示出来。那么可以将添加调查和编辑调查文件合并处理,只需要增加一个判断就可以了。代码如下:
$vote = new Vote();
if($_GET[‘id’]) //判断是否有编辑信息的ID
{
$info = $vote->getInfo($_GET['id'],$vote->_name); //提取编辑信息内容
}
……
if($_SERVER['REQUEST_METHOD'] == 'POST')
{
$data['F_VOTE_TITLE'] = $_POST['title'];
$data['F_VOTE_START'] = mktime(0,0,0,$_POST['start_m'],$_POST['start_d'],$_POST['start_y']);
$data['F_VOTE_END'] = mktime(0,0,0,$_POST['end_m'],$_POST['end_d'],$_POST['end_y']);
$data['F_VOTE_ITEM_TYPE'] = $_POST['type'];
$data['F_VOTE_IS_DISPLAY'] = $_POST['display'];
if($_POST[‘id’]) //判断是否有编辑信息的ID
{
if($vote->updateData($vote->_name,$_POST['id'],$data)) //执行编辑操作
{
echo "操作成功";
echo "<a href='VoteList.php'>返回</a>";
exit();
}
}else{
if($vote->insertData($vote->_name,$data)) //执行添加操作
{
echo "操作成功";
echo "<a href='VoteList.php'>返回</a>";
exit();
}
}
}
1.4.5 删除调查文件DelVote.php
该文件的功能是删除调查及相关数据。该文件由图1.3中调查列表页面的操作列“[删除]”连接过来传递调查ID参数。删除数据通过调查类Vote的对象调用父类DBSQL的delData()方法来实现。代码如下:
<?php
require_once('../config.inc.php');
require_once(INCLUDE_PATH . 'vote.inc.php');
$vote = new Vote();
if($vote->delData($_GET['id']))
{
echo "操作成功<br>";
echo "<a href=' VoteList.php'>返回</a>";
exit();
}else{
echo "操作失败<br>";
echo "<a href=' VoteList.php'>返回</a>";
exit();
}
?>
1.4.6 调查选项列表文件ItemList.php
该文件的功能是显示调查选项表1.2中的数据列表。该文件由图1.3中调查列表页面的操作列“[选项管理]”连接过来传递调查ID参数。该文件通过类Vote的对象调用父类的getItemList()方法实现的。通过这个页面可以连接到添加、编辑选项、删除选项和设置选项顺序页面。界面如图1.6所示。
图1.6 调查选项列表
代码如下:
<?php
require_once('../config.inc.php');
require_once(INCLUDE_PATH . 'vote.inc.php');
$vote = new Vote();
$list = $vote->getItemList($_GET['id']);
?>
<table width="60%" border="0" align="center">
$info = $vote->getInfo($_GET['id'],$vote->_name);
<td>调查标题:<?php echo $info['F_VOTE_TITLE']?> </td>
</table>
<table width="60%" border="0" align="center" cellspacing="0" class="l_table_1" id="table_1">
<tr class="title">
<td width="10%">序号</td>
<td width="50%">选项标题</td>
<td width="19%">统计</td>
<td width="21%">操作</td>
</tr>
<?php
if($list)
{
foreach($list as $key => $value)
{
?>
<tr class="l_field">
<td align="left"><?php echo ($key + 1)?></td>
<td align="left"><?php echo $value['F_ITEM_TITLE']?></td>
<td align="left"><?php echo $value['F_ITEM_COUNT']?> 次 </td>
<td align="left"><a href="EditItem.php?id=<?php echo $value['F_ID']?>&voteid=<?php echo $value['F_ID_VOTE_INFO']?>">[编辑]</a><a href="DelItem.php?id=<?php echo $value['F_ID']?>&voteid =<?php echo $_GET['id']?>"> [删除]</a> </td>
</tr>
<?php
}
}
?>
<tr>
<td colspan="9" align="center"><input type="submit" name="Submit3" value="添加选项" /> <input type="button" name="Submit" value="设置顺序" /></td>
</tr>
</table>
1.4.7 添加、编辑调查选项文件OperItem.php
该文件的功能是增加指定调查的选项和编辑指定调查选项的信息。添加调查选项界面如图1.7所示。该文件由图1.6中调查选项列表页面的“添加选项”按钮连接过来传递调查ID参数。编辑调查选项界面如图1.8所示。该文件由图1.6中调查选项列表页面的操作列“[编辑]”连接过来传递调查ID参数、选项ID参数。这里添加和编辑作为一个文件来处理。判断当有选项ID参数传递时为编辑状态,提取该选项信息并显示。用户编辑或添加完表单数据后单击“提交”按钮进行数据处理。数据处理通过调查类Vote的对象调用父类的insertData()方法实现的,通过调用updateData()方法实现编辑。
图1.7 添加调查选项
图1.8 编辑调查选项
代码如下:
<?php
require_once('../config.inc.php');
require_once(INCLUDE_PATH . 'vote.inc.php');
$vote = new Vote();
$title ="添加调查选项"
if($_GET['id']) //判断是否有编辑选项ID,有则提取记录
{
$info = $vote->getInfo($_GET['id'],$vote->_item);
$title ="编辑调查选项";
}
if($_SERVER['REQUEST_METHOD'] == 'POST') //判断是否提交请求
{
$data['F_ID_VOTE_INFO'] = $_POST['voteid'];
$data['F_ITEM_TITLE'] = $_POST['title'];
$data['F_ITEM_COUNT'] = 0;
$data['F_ITEM_ORDER'] = 0;
if($_POST['id']) //有选项ID参数,则执行编辑操作
{
if($vote->updateData($vote->_item,$_POST['id'],$data)) //判断是否操作成功
{
echo "添加操作成功<br>";
echo "<a href='ItemList.php?id={$_POST['voteid']}'>返回</a>";
exit();
}else{
echo "添加操作失败<br>";
echo "<a href='ItemList.php?id={$_POST['voteid']}'>返回</a>";
exit();
}
}else{ //无选项ID参数,则执行添加操作
if($vote->insertData($vote->_item,$data)) //判断是否操作成功
{
echo "编辑操作成功<br>";
echo "<a href='ItemList.php?id={$_POST['voteid']}'>返回</a>";
exit();
}else{
echo "编辑操作失败<br>";
echo "<a href='ItemList.php?id={$_POST['voteid']}'>返回</a>";
exit();
}
}
}
?>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td height="32" background="/images/lefttop.jpg" class="head">调查管理>{$title}</td>
</tr>
</table>
<form name="form1" action="" method="post" >
<table width="60%" border="0" align="center" cellpadding="0" cellspacing="0" class="l_table">
<tr class="title">
<td colspan="2" align="left"> </td>
</tr>
<tr class="l_field">
<td width="24%" align="right">调查标题:</td>
<td width="76%">您对网站的满意程度?</td>
</tr>
<tr class="l_field">
<td align="right">选项标题:</td>
<td><input name="title" type="text" id="title" size="40" value="<?php echo $info['F_ITEM_TITLE']?>" /></td>
</tr>
<tr class="title">
<td colspan="2" align="center"><input type="submit" name="Submit" value="提交" />
<input type="reset" name="reset" value="重置" />
<input type='hidden' name="id" id="id" value="<?php echo $_GET['id']?>">
<input type='hidden' name="voteid" id="voteid" value="<?php echo $_GET['voteid']?>">
</td>
</tr>
</table>
<table align="center" border="0" cellpadding="0" cellspacing="0" width="98%">
<tbody><tr>
<td> </td>
</tr>
</tbody></table>
</form>
<script language="javascript">
function check()
{
if(document.form1.title.value == "") //判断标题是否为空,是则弹出提示框
{
alert('请填写选项标题');
document.form1.focus();
return false;
}
return true;
}
</script>
1.4.8 选项顺序设置文件SetOrder.php
该文件的功能是设置调查选项的显示顺序。该文件由图1.6中调查选项列表页面的“设置顺序”连接过来传递调查ID参数。用户在顺序文本框中输入顺序数值后单击“提交”按钮进行数据处理。选项按数值顺序从大到小排列。数据提交到本页判断是否为提交操作并进行处理。数据的处理通过调查类Vote的对象调用父类的setOrder()方法实现的。选项顺序设置界面如图1.9所示。
图1.9 选项顺序设置
在调查类文件vote.inc.php中加入下列代码:
/**
* 功能:设置指定调查的选项顺序
* 参数:$id 选项ID数组,$order 选项顺序数组
* 返回:TRUE OR FALSE
*/
public function setOrder($id,$order)
{
if($id)
{
$this->begintransaction();
try {
foreach ($id as $key => $value)
{
$sql = "UPDATE " . $this->_item . " SET F_ITEM_ORDER = {$order[$key]} WHERE F_ID = $value";
$this->update($sql);
}
}catch (Exception $e)
{
$this->rollback();
return false;
}
$this->commit();
}
return true;
}
下面这段代码定义了一个设置调查选项顺序的函数,这个函数在选项顺序设置文件中被调用。代码如下:
<?php
require_once('../config.inc.php');
require_once(INCLUDE_PATH . 'vote.inc.php');
$vote = new Vote();
$list = $vote->getItemList($_GET['voteid']);
$info = $vote->getInfo($_GET['voteid'],$vote->_name);
if($_SERVER['REQUEST_METHOD'] == 'POST')
{
if($vote->setOrder($_POST['id'],$_POST['order']))
{
echo "操作成功<br>";
echo "<a href='ItemList.php?id={$_GET['voteid']}'>返回</a>";'SetOrder.php?id={$_GET['voteid']}'>返回</a>";
exit();
}else{
echo "操作失败<br>";
echo "<a href=' ItemList.php?id={$_GET['voteid']}'>返回</a>";'SetOrder.php?id={$_GET['voteid']}'>返回</a>";
exit();
}
}
?>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td height="32" background="/images/lefttop.jpg" class="head">调查管理>调查选项顺序设置</td>
</tr>
</table>
<form name="form1" action="" method="post">
<table width="60%" border="0" align="center">
<tr>
<td>调查标题:<?php echo $info['F_VOTE_TITLE']?></td>
</tr>
</table>
<table width="60%" border="0" align="center" cellspacing="0" class="l_table_1" id="table_1">
<tr class="title">
<td width="10%">序号</td>
<td width="63%">选项标题</td>
<td width="27%">顺序</td>
</tr>
<?php
if($list)
{
foreach ($list as $key => $value)
{
?>
<tr class="l_field">
<td align="left"><?php echo ($key+1)?>
<input name="id[]" type="hidden" id="id[]" value="<?php echo $value['F_ID']?>" /></td>
<td align="left"><?php echo $value['F_ITEM_TITLE']?></td>
<td align="left"><input name="order[]" type="text" id="order[]" value="<?php echo $value['F_ITEM _ORDER']?>" size="10" /></td>
</tr>
<?php
}
}
?>
<tr>
<td colspan="8" align="center"><input type="submit" name="Submit" value=" 提 交 设 置 " /><input type= "hidden" name="voteid" id="voteid" value="<?php echo $_GET['id']?>"></td>
</tr>
</table>
</form>
1.4.9 删除调查选项文件DelItem.php
该文件的功能是删除指定选项记录。该文件由图1.6中调查选项列表页面的操作列“[删除]”连接过来传递调查ID参数、选项ID参数。选项的删除通过类Vote的对象调用父类的delItemData()方法来实现。代码如下:
<?php
require_once('../config.inc.php');
require_once(INCLUDE_PATH . 'vote.inc.php');
$vote = new Vote();
if($vote->delItemData($_GET['id'])) //判断是否操作成功
{
echo "操作成功<br>";
echo "<a href='ItemList.php?id={$_GET['voteid']}'>返回</a>";
exit();
}else{
echo "操作失败<br>";
echo "<a href='ItemList.php?id={$_GET['voteid']}'>返回</a>";
exit();
}
?>
1.4.10 用户统计信息列表文件UserList.php
该文件的功能是提取指定调查的用户统计信息列表。该文件由图1.3中调查列表页面的操作列“[用户统计信息]”连接过来传递调查ID参数。用户统计信息列表以分页显示,每页显示10条记录。分页的实现通过页码来计算每页提取记录的开始位置。计算公式为开始位置=(页码-1)×每页显示数量。界面如图1.10所示。
图1.10 调查用户统计信息
代码如下:
<?php
require_once('../config.inc.php');
require_once(INCLUDE_PATH . 'vote.inc.php');
$vote = new Vote();
$cur_page = $_GET['page']; //取得当前页码
if(!$cur_page)
$cur_page = 1; //如果无页码,则默认为第一页
$list = $vote->getUserList($_GET['id'],$cur_page);
$count = $vote->getUserCount($_GET['id']);
$pagecount = ceil($count/$vote->_pagesize); //计算总共的页数
if(!$pagecount)
$pagecount = 1; //如果无总页数,则默认为1
$url = "?id={$_GET['id']}&page="; //翻页跳转的地址
?>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td height="32" background="/images/lefttop.jpg" class="head">调查管理>用户统计信息列表</td>
</tr>
</table>
<form name="form1" action="" method="post">
<table width="80%" border="0" align="center">
<tr>
<td><a href="AreaList.php?id=<?php echo $_GET['id']?>">[点击查看区域统计]</a></td>
</tr>
</table>
<table width="80%" border="0" align="center" cellspacing="0" class="l_table_1" id="table_1">
<tr class="title">
<td width="7%">序号</td>
<td width="45%">用户IP</td>
<td width="25%">所在区域</td>
<td width="23%">投票时间</td>
</tr>
<?php
if($list)
{
foreach ($list as $key => $value)
{
?>
<tr class="l_field">
<td align="left"><?php echo ($key + 1)?></td>
<td align="left"><?php echo long2ip($value['F_USER_IP'])?></td>
<td align="left"><?php echo $value['F_USER_AREA']?></td>
<td align="left"><?php echo date("Y-m-d,H:i:s",$value['F_USER_TIME'])?></td>
</tr>
<?php
}
}
?>
<tr>
<td colspan="9" align="center"><table width='100%' align='center' border='0' cellspacing='0'>
<tr>
<td align="left"> 共有 <b><?php echo $count?></b> 信息共 <font color='#FF0000'><b><?php echo $cur_page?></b></font> / <b><?php echo $pagecount?></b>页 每页<strong><?php echo $vote->_pagesize?> </strong></td>
<td width="30">转到</td>
<td width="50"><select name="page" onchange="javascript:location.href= document. getElementById('url')+this.options[selectedIndex].value">
<?php
for($i=1;$i<=$pagecount;$i++)
{
echo "<option value='$i'";
if($i == $cur_page)
echo " selected='selected'";
echo ">$i</option>";
}
?>
</select>
<input type="hidden" name="url" value="<?php echo $url?>" /></td>
<td width="15">页 </td>
</tr>
</table></td>
</tr>
</table>
</form>
1.4.11 区域统计文件AreaList.php
在用户统计信息文件里显示了统计信息的列表,但这个列表对信息的统计不能一目了然。区域统计文件的功能就是按区域统计用户的信息。该文件是由图1.10中用户统计信息页面的“[点击查看区域统计]”连接过来传递调查ID参数。界面如图1.11所示。
图1.11 区域统计
在调查类文件vote.inc.php中加入下列代码:
/**
* 功能:按区域统计用户信息
* 参数:$id 调查ID
* 返回:数组
*/
public function areaList($id)
{
$sql = "SELECT COUNT(F_ID) AS C,F_USER_AREA FROM " . $this->_user;
$sql .= " WHERE F_ID_VOTE_INFO = $id GROUP BY F_USER_AREA";
return $this->select($sql);
}
上面代码定义了按区域分组查询用户统计信息表的函数,这个函数在区域统计文件中被调用。代码如下:
<?php
require_once('../config.inc.php');
require_once(INCLUDE_PATH . 'vote.inc.php');
$vote = new Vote();
$list = $vote->areaList($_GET['id']);
?>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td height="32" background="/images/lefttop.jpg" class="head">调查管理>区域统计</td>
</tr>
</table>
<form name="form1" action="" method="post">
<table width="60%" border="0" align="center" cellspacing="0" class="l_table_1" id="table_1">
<tr class="title">
<td width="9%">序号</td>
<td width="58%">区域</td>
<td width="33%">次数</td>
</tr>
<?php
if($list)
{
foreach ($list as $key => $value)
{
?>
<tr class="l_field">
<td align="left"><?php echo ($key + 1)?></td>
<td align="left"><?php echo $value['F_USER_AREA']?></td>
<td align="left"><?php echo $value['C']?></td>
</tr>
<?php
}
}
?>
</table>
</form>
1.5 调查显示模块
调查显示模块的功能是把符合条件的调查提取并显示出来。这里的条件包括是否启用和是否过期。调查显示界面如图1.12所示。
图1.12 调查显示
这里需要调查类文件vote.inc.php里有一个按条件提取调查信息的方法,因此不需要新增方法,而只需要修改原来的getVoteList方法即可。代码如下:
/**
* 功能:提取调查列表
* 参数:$where 查询条件
* 返回:数组
*/
public function getVoteList($where = ''){
$sql = "SELECT * FROM" . $this->_name;
if($where) //如果参数有值,则加入查询条件
{
$sql .= "WHERE" . $where;
}
return $this->select($sql);
}
调查显示代码如下:
<?php
require_once("config.inc.php");
require_once(INCLUDE_PATH . "vote.inc.php");
$vote = new Vote();
$time = time();
$where = "F_VOTE_IS_DISPLAY = 1 AND F_VOTE_END < $time";
$list = $vote->getVoteList($where);
if($list)
{
foreach($list as $key => $value) //循环显示调查
{
$item = $vote->getItemList($value['F_ID']); //提取指定调查的选项
?>
<form name="form_<?php echo $value['F_ID']?>" method="post" action="Deal.php">
<table border="0" align="center" cellpadding="0" cellspacing="0">
<tr>
<td><strong><?php echo ($key+1)?>. <?php echo $value['F_VOTE_TITLE']?></strong></td>
</tr>
<tr>
<td>
<?php
if($item)
{
foreach($item as $val) //循环显示调查选项
{
if($value['F_VOTE_TYPE'] == 1) //判断调查选项的形式,以不同方式显示
echo "<input type='radio' name='item' value='{$val['F_ID']}'>";
else
echo "<input name='item[]' type='checkbox' id='item[]' value='{$val['F_ID']}' />";
echo $val['F_ITEM_TITLE'] . "<br>";
}
}
?>
</td>
</tr>
<tr>
<td><input type="submit" name="Submit" value="提交">
<input type="button" name="Submit2" value="查看结果">
<input type="hidden" name="id" value="<?php echo $value['F_ID']?>">
</td>
</tr>
</table>
</form>
<?php
}
}
?>
这里需要注意的是:由于可能会有几个调查同时满足条件,那么就会有多个表单,这些表单的名称不能相同。本例中的处理方法是将表单的名称写成form_加调查ID的方式。
1.6 投票处理模块
投票处理模块的功能是对投票用户的信息进行合法性检查,把合法用户的投票数据写入数据库。需要提取的用户信息是用户的IP,通过IP来匹配用户所在区域。现在最流行的通过IP来匹配区域的方式就是利用QQWry.dat来检索区域。本例中直接调用该方法。代码如下:
<?php
require_once("config.inc.php");
require_once(INCLUDE_PATH . "vote.inc.php");
require_once(INCLUDE_PATH . "ip.inc.php");
$vote = new Vote();
$ipclass = new Ip();
$ip = getenv("REMOTE_ADDR");
$ip1 = getenv("HTTP_X_FORWARDED_FOR");
$ip2 = getenv("HTTP_CLIENT_IP");
($ip1) ? $ip = $ip1 : null ; //$ip1有值则赋给ip
($ip2) ? $ip = $ip2 : null ; //$ip2有值则赋给ip
$longip = ip2long($ip); //将IP转化为整数
if(!isset($_POST['item'])) //判断用户是否选择了选项
{
echo "<script>alert('您未选择调查选项!');window.history.back();</script>";
exit();
}
if($vote->checkIsValid($longip,$_POST['id'])) //判断用户是否合法
{
$location = $ipclass-> ip2location getlocation($longip); //匹配IP地址取得所在地区
$vote->updateResult($_POST['item'],$_POST['id'],$longip,$location); //更新数据库表
header("Location:Result.php?id={$_POST['id']}"); //跳转到结果页面
}else{
echo "<script>alert('您已经投过票了!'),windows.location='Result.php?id={$_POST['id']}'</script>"; //不合法则提醒并跳转到结果页面
exit();
}
?>
上面代码在取得IP时用了3个预定义变量,分别是REMOTE_ADDR、HTTP_X_FORWARDED_
FOR和HTTP_CLIENT_IP。getenv(REMOTE_ADDR)用于取得客户端地址,如果客户端用的是代理(客户端IP为127.0.0.1,代理IP为61.135.150.76),那么这个方法取得的地址为127.0.0.1。getenv(HTTP_X_FORWARDED_FOR)可以取得代理地址,但是HTTP_X_FORWARDED_FOR是HTTP头协议的一部分,很容易伪造,所以这里用到了HTTP_CLIENT_IP。
这段代码调用了checkIsValid()方法用来检查用户的合法性,调用updateResult()方法用来更新数据表,这是在vote.inc.php里面定义的。代码如下:
/**
* 功能:检查用户合法性
* 参数:$ip 用户IP,$voteid 调查ID
* 返回:TRUE OR FALSE
*/
public function checkIsValid($ip,$voteid)
{
$sql = "SELECT F_ID FROM " . $this->_user . " WHERE F_USER_IP = $ip AND F_ID_VOTE_INFO = $voteid";
$r = $this->select($sql);
if($r[0][F_ID] > 0) //如果该IP已经为该调查投过票,则返回false
{
return false;
}else{
return true;
}
}
/**
* 功能:处理投票数据和用户信息更新相关数据表
* 参数:$item 调查选项ID,$voteid 调查ID,$ip 用户IP,$location 用户所在区域
* 返回:TRUE
*/
public function updateResult($item,$voteid,$ip,$location)
{
if(is_array($item)) //判断item是否是数组,是则为多选调查,循环处理数据
{
$this->begintransaction(); //开始事务处理
try {
foreach ($item as $value)
{
$sql = "UPDATE " . $this->_item . " SET F_ITEM_COUNT = F_ITEM_COUNT + 1 WHERE F_ID = $value";
$this->update($sql);
}
$data = array();
$data['F_USER_IP'] = $ip;
$data['F_USER_TIME'] = time();
$data['F_USER_AREA'] = $location;
$data['F_ID_VOTE_INFO'] = $voteid;
$this->insertData($this->_user,$data);
}catch (Exception $e){ //出现异常,则回滚
$this->rollback();
}
$this->commit(); //正常提交
return true;
}else{
$this->begintransaction(); //开始事务处理
try {
$sql = "UPDATE " . $this->_item . " SET F_ITEM_COUNT = F_ITEM_COUNT + 1 WHERE F_ID = $item";
$this->update($sql);
$data = array();
$data['F_USER_IP'] = $ip;
$data['F_USER_TIME'] = time();
$data['F_USER_AREA'] = $location;
$data['F_ID_VOTE_INFO'] = $voteid;
$this->insertData($this->_user,$data);
}catch (Exception $e){ //出现异常,则回滚
$this->rollback();
}
$this->commit(); //正常提交
return true;
}
}
1.7 调查结果显示模块
调查结果显示模块的功能是把调查结果按比例显示。界面如图1.13所示。这个页面是由投票处理页面跳转或单击调查显示页面里面的“查看结果”按钮连接过来传递调查ID参数。比例计算是用单个选项的投票数除以所有投票数。比例柱图的显示长度就是每个选项的比例。
图1.13 调查结果
代码如下:
<?php
require_once("config.inc.php");
require_once(INCLUDE_PATH . "vote.inc.php");
$vote = new Vote();
if($_GET['id']) //判断是否有传递参数
{
$info = $vote->getInfo($_GET['id'],$vote->_name);
if(!isset($info[F_ID])) //判断此ID的调查是否存在
{
echo "无此调查";
exit();
}
$item = $vote->getItemList($_GET['id']);
$sum = 0;
foreach ($item as $value) //计算总投票数
{
$sum += $value['F_ITEM_COUNT'];
}
}else{ //无参数则提示
echo "参数错误";
exit();
}
?>
<table width="50%" border="0" align="center">
<tr>
<td><?php echo $info['F_VOTE_TITLE']?></td>
</tr>
</table>
<table width="50%" border="0" align="center" cellpadding="3" cellspacing="2">
<?php
if($item)
{
foreach ($item as $value)
{
$percent = @number_format($value['F_ITEM_COUNT']/$sum,2); //计算每个选项票数所占比例
$length = $percent . "%";
?>
<tr>
<td width="27%"><font color="#000000"><?php echo $value['F_ITEM_TITLE']?></font></td>
<td width="32%"><table height="12" cellspacing="1" cellpadding="0" width="<?php echo $length?>"
bgcolor="#000000" border="0">
<tr>
<td bgcolor="#FF0000"> </td>
</tr>
</table></td>
<td width="41%"><span class="STYLE2"><?php echo $value['F_ITEM_COUNT']?></span><font color="#000000"> <?php echo $length?></font></td>
</tr>
<?php
}
}
?>
</table>
这个模块里面需要注意的是:判断传递参数的有效性及参数是否为空,参数是否有效。本例中就是判断传递的调查ID是否为空,该调查是否存在。还有一点就是计算每个选项所占比例时,需要加上@符号以屏蔽错误提示。因为当总票数为0时,进行除法计算就会报错,这样浏览者会感觉系统容错性不好。
1.8 小 结
本章讲解了投票系统的开发,重点是数据库连接和表的基本操作以及投票系统的模块分析,合理地设计系统。难点在于投票的处理和用户信息的统计。投票的处理需要进行用户信息的合法性检测和投票数据的合法性检测以保证投票的唯一性和正确性。用户信息的统计能使站点了解访问用户的区域分布情况。
|
|