CREATE TABLE `article` ( `id` INT NOT NULL AUTO_INCREMENT, `title` VARCHAR( 100 ) NOT NULL , `content` TEXT NOT NULL , `datetime` DATETIME NOT NULL , `clicks` INT( 11 ) , `pid` TINYINT( 2 ) NOT NULL , PRIMARY KEY ( `id` ) );
CREATE TABLE `cat` ( `cid` TINYINT( 2 ) NOT NULL , `cname` VARCHAR( 20 ) NOT NULL , PRIMARY KEY ( `cid` ) );
------------------------------
article表是文章内容表,
----------------------------
`id` 文章编号
`title` 文章标题
`content` 文章内容
`datetime` 发布时间
`clicks` 点击数
`pid` 分类表号
------------------------------
cat表是文章的类别表
----------------------------
`cid` 分类表号
`cname` 分类名称
----------------------------
上面是表的数据库结构,光有了这些还不够,还要有数据
INSERT INTO `cat` VALUES(1, "php开发"),(2, "asp开发"); INSERT INTO `article` VALUES(1, "php开发1", "php开发1内容", "2004-8-1 1:1:1", 0, 1); INSERT INTO `article` VALUES(2, "php开发2", "php开发2内容", "2004-8-2 1:1:1", 0, 1); INSERT INTO `article` VALUES(3, "php开发3", "php开发3内容", "2004-8-3 1:1:1", 4, 1); INSERT INTO `article` VALUES(4, "php开发4", "php开发4内容", "2004-8-4 1:1:1", 3, 1); INSERT INTO `article` VALUES(5, "php开发5", "php开发5内容", "2004-8-5 1:1:1", 2, 1); INSERT INTO `article` VALUES(6, "php开发6", "php开发6内容", "2004-8-6 1:1:1", 1, 1); INSERT INTO `article` VALUES(7, "php开发7", "php开发7内容", "2004-8-7 1:1:1", 0, 1); INSERT INTO `article` VALUES(8, "jsp开发1", "jsp开发1内容", "2004-8-1 1:1:1", 0, 2); INSERT INTO `article` VALUES(9, "jsp开发2", "jsp开发2内容", "2004-8-2 1:1:1", 0, 2); INSERT INTO `article` VALUES(10, "jsp开发3", "jsp开发3内容", "2004-8-3 1:1:1", 4, 2); INSERT INTO `article` VALUES(11, "jsp开发4", "jsp开发4内容", "2004-8-4 1:1:1", 3, 2); INSERT INTO `article` VALUES(12, "jsp开发5", "jsp开发5内容", "2004-8-5 1:1:1", 2, 2); INSERT INTO `article` VALUES(13, "jsp开发6", "jsp开发6内容", "2004-8-6 1:1:1", 1, 2); INSERT INTO `article` VALUES(14, "jsp开发7", "jsp开发7内容", "2004-8-7 1:1:1", 0, 2);
这样我们的数据库就设计完了。接下来就开始涉及到具体的实现了。
四、设计config.inc.php文件
这个文件用来设置一些web上通用的数据信息和一些参数,其他的具体的实现页面都通过这个页面获取需要的数据,下面是配置的清单
其中的 define('CMS_ROOT', 'C:/Apache2/htdocs/cmstest/'); 路经根据自己apach的web路经来改(参照最开始介绍文件夹结构的地方改)。
五、制作功能接口(1)
首先对 mysql 数据库函数进行包装,简化对数据库操作,网上有很多这样的开源的类。但是这里我个人根据自己的需求和习惯,自己对 mysql 的函数进行了包装,写得好坏就先不管了。这个地方简单的看一下就可以了,不同的包装的类操作是不同的,而且这里的主要目的是理解这套“架构”,不用太扣代码。
-------MysqlUtil.php--------
0) return mysql_fetch_assoc($rs); else return null; } function dbGetOne($sql, $fildName){ $rs = dbGetRow($sql); return sizeof($rs)==null? null: (isset($rs[$fildName])? $rs[$fildName]: null); } function &dbPageQuery($sql, $page=1, $pageSize=20){ if($page===null) return dbQuery($sql); $countSql = preg_replace(&#39;|SELECT.*FROM|i&#39;,&#39;SELECT COUNT(*) count FROM&#39;, $sql); $n = (int)dbGetOne($countSql, &#39;count&#39;); $data[&#39;pageSize&#39;] = (int)$pageSize<1? 20: (int)$pageSize; $data[&#39;recordCount&#39;] = $n; $data[&#39;pageCount&#39;] = ceil($data[&#39;recordCount&#39;]/$data[&#39;pageSize&#39;]); $data[&#39;page&#39;] = $data[&#39;pageCount&#39;]==0? 0: ((int)$page<1? 1: (int)$page); $data[&#39;page&#39;] = $data[&#39;page&#39;]>$data[&#39;pageCount&#39;]? $data[&#39;pageCount&#39;]:$data[&#39;page&#39;]; $data[&#39;isFirst&#39;] = $data[&#39;page&#39;]>1? false: true; $data[&#39;isLast&#39;] = $data[&#39;page&#39;]<$data[&#39;pageCount&#39;]? false: true; $data[&#39;start&#39;] = ($data[&#39;page&#39;]==0)? 0: ($data[&#39;page&#39;]-1)*$data[&#39;pageSize&#39;]+1; $data[&#39;end&#39;] = ($data[&#39;start&#39;]+$data[&#39;pageSize&#39;]-1); $data[&#39;end&#39;] = $data[&#39;end&#39;]>$data[&#39;recordCount&#39;]? $data[&#39;recordCount&#39;]: $data[&#39;end&#39;]; $data[&#39;sql&#39;] = $sql.&#39; LIMIT &#39;.($data[&#39;start&#39;]-1).&#39;,&#39;.$data[&#39;pageSize&#39;]; $data[&#39;data&#39;] = &dbQuery($data[&#39;sql&#39;]); return $data; } function dbExecute($sql){ global $cnn; mysql_query($sql, $cnn) or die(&#39;sql语句执行错误&#39;); return mysql_affected_rows($cnn); } function dbDisconnect(){ global $cnn; mysql_close($cnn); } function sqlGetOneById($table, $field, $id){ return "SELECT * FROM $table WHERE $field=$id"; } function sqlMakeInsert($table, $data){ $t1 = $t2 = array(); foreach($data as $key=>$value){ $t1[] = $key; $t2[] = "&#39;".addslashes($value)."&#39;"; } return "INSERT INTO $table (".implode(",",$t1).") VALUES(".implode(",",$t2).")"; } function sqlMakeUpdateById($table, $field, $id, $data){ $t1 = array(); foreach($data as $key=>$value){ $t1[] = "$key=&#39;".addslashes($value)."&#39;"; } return "UPDATE $table SET ".implode(",", $t1)." WHERE $field=$id"; } function sqlMakeDelById($table, $field, $id){ return "DELETE FROM $table WHERE $field=$id"; } ?>
五、制作功能接口(2)
下面来正式的看看,我们共要实现的功能进行的包装
------------ArticleUtil.php----------------
这段代码是不是就简单多了啊?这就是自己对mysql函数进行包装的好处!
下面来研究一下他们是怎么实现我们的功能的呢。
“php开发文章列表”--------getArticleList(1, "id DESC", $page, 5)
“asp开发文章列表”--------getArticleList(2, "id DESC", $page, 5)
“php开发热点文章列表”----getArticleList(1, "clicks DESC, id DESC", 1, 3)
“asp开发热点文章列表”----getArticleList(2, "clicks DESC, id DESC", 1, 3)
“asp开发最新文章”--------getArticleList(2, "id DESC", 1, 3)
“添加新文章”-------------addArticle($data)
“察看文章”---------------getArticle($id)
六、对smarttemplate类进行包装(革命尚未成功,同志仍须努力)
具体的smarttemplate的使用这里就不讲了,不然口水讲没了,都讲不完。下面这个是具体的对包装函数
-------------ParseTpl.php----------------
$value){ if(isset($value[data])){ $page->assign($key, $value[data]); unset($value[data]); $page->assign($key."_page", $value); } else { $page->assign($key, $value); } } $page->output(); } ?>
七:文章列表察看页面实现和模板处理(万里长征的最后一步)
先来看看页面list1的实现,在list1里面分页用了一个page.js文件,这个文件是自己给自己写的一个js分页的函数,挺好用的
---------------page.js---------------
//--------共 20 条记录,当前 86/99 页 [1]... [82] [83] [84] [85] [86] [87] [88] [89] [90] ...[99] GO------------------- //recordCount = 20; //show = 20 //pageShow = 11; //pageCount = 100; //pageNow = 86; //pageStr = "?page=_page_"; //document.write(showListPage(recordCount, show, pageCount, pageNow, pageStr)); function showListPage(recordCount, show, pageShow, pageCount, pageNow, pageStr){ if(pageCount<1) pageCount =0; if(pageNow<1) pageNow = 0; str = &#39;共 &#39;+recordCount+&#39; 条记录,当前 &#39;+pageNow+&#39;/&#39;+pageCount+&#39; 页 &#39;; if(pageCount<=pageShow){ startHave = false; endHave = false; startNum = 1; endNum = pageCount; } else if(pageNow-1 <= pageShow/2){ startHave = false; endHave = true; startNum = 1; endNum = pageShow-1; } else if(pageCount-pageNow <= pageShow/2){ startHave = true; endHave = false; startNum = pageCount - pageShow + 2; endNum = pageCount; } else { startHave = true; endHave = true; startNum = pageNow - Math.floor((pageShow-2)/2); endNum = startNum + pageShow - 3; } if(startHave){ startStr = " [1]... "; str += startStr; } for(i=startNum; i<=endNum; i++){ if(pageNow==i) str += "[" + i + "]"; else str += " [" + i + "] "; } if(endHave){ endStr = " ...[" + pageCount + "] "; str += endStr; } return str; }
--------------list1.htm----------------
添加新文章
php开发文章 |
---|
{id}--{title} |
php开发热点文章 |
---|
{id}--{title} |
asp开发最新文章 |
---|
{id}--{title} |
--------------list1.php----------------
运行的效果怎么样,是不是实现了要求的功能呢。现在我们再做一下改动,在里面加上“asp开发热点文章列表”,实现代码如下
--------------list1.htm----------------
添加新文章
php开发文章 |
---|
{id}--{title} |
php开发热点文章 |
---|
{id}--{title} |
asp开发最新文章 |
---|
{id}--{title} |
asp热点文章 |
---|
{id}--{title} |
--------------list1.php----------------
仔细观察一下前后的区别,list1.php里面只是简单的加入了一行的代码,就实现这个改动,感觉怎么样啊?是不是超级简单。
其实这种设计模式的好处还不只是这点:
1、可以把程序的核心代码隔离开管理,便于以后程序的管理维护
2、对于程序的可扩展性也很好,假设list1.php中要加入产品列表,我是不是也可以这么做呢?把对产品的管理也写成统一的数据库操作接口,然后简单的修改模板文件加入产品列表部分,最后在list1.php中加入一行函数调用的代码,就可以实现。
3、代码复用,如果您是做中小型企业网站的,那这么做对您的好处是最大的,因为这种类型的网站的设计结构几乎是一样的,您可能只需要更改一下模板的样式,就可以赚到钞票了。
这么看来这种模式是不是给您带来了很多的好处呢?
-----------lsit2.htm---------------
添加新文章
asp开发文章 |
---|
{id}--{title} |
asp热点文章 |
---|
{id}--{title} |
-----------lsit2.php---------------
--------view.htm--------------
编号:{id}
标题:{title}
内容:{content}
--------view.php------------------
八:文章添加实现和模板处理(万里长征的再来一步)
---------------new.htm-----------
标题:
内容:
---------------new.php------------
---------------CoreUtil.php--------------
---------------add.htm------------
{content}
---------------add.php------------
这样一个最最简单的文章的发布系统就完成了,不知道对您有什么收获没有。
九、总结
程序写完了,大家来总结一下吧,看看实现过程,应该可以说是简单明了吧。
1、统一实现数据库访问接口。更改后台数据库结构的时候,只要简单的修改相应的接口函数,其他部分的php代码根本就不用理会。
2、整个系统php代码和html代码分开管理,php代码前台实现起来也很简单,你也应该已经可以发现了,基本上是在10行代码左右。
3、可以任意的扩充功能。用函数对新的功能进行包装后,在具体的前台的查询显示页面中只有加入对相应的函数的简单的调用就可以了,一行代码就搞定了。
4、代码复用。通过对功能的包装,减少了大量的不必要的重复代码工作,效率应该是提高了很多吧?
5、可以移植性。如果将来做其他的网站,要是遇到了和这个项目相同的实现功能,你可以怎么做呢?把这里的函数和数据库结构copy过去,修改一下新项目的模板,是不是就可一完成这个新的项目了呢?根本就不用考虑修改php代码。
6、结构代码清晰。别人可以轻易的和你共同开发一个项目,代码冲突也会减少至最低。
当然实际工作中遇到的情况可能比这个例子复杂的多,但是再复杂的任务都是可以拆分的。
十、后言
不知道大家有没有学过jsp和servlet这样的东西,他们有很多优秀的设计思路,值得我们去研究和copy(说句实在话,有时间的都应该去接触一下java,不是为了学习这门语言而学习,是要学习他的设计模式和优秀的功能实现方式)。jsp至少有两样东西对我们很有用,action和filter。
action是主要是处理一些事件逻辑,就像add.php中我们定义的action()函数一样,用来验证和获取表单提交上来的数据。当然这样写的代码量和你以前方法的代码量是一样的,好像没有什么区别,但是它实现了代码的分离,结构是不是比你以前的方法清晰很多呢?
filter是一个过滤功能,用来过滤和重新定向网络的访问,对一些非法的请求和错误的请求进行重新的定向。让我们来通过一个后台管理的程序来看看他具体的作用。
将下面的这个函数copy到你的CoreUtil.php里面,这个函数是一个后台管理登陆的过滤函数,一个是不在本地缓存web页面的函数:
function windowNoCache($cache){ if(!$cache || headers_sent()) return ; header(&#39;Expires: &#39;.date(&#39;D,d M Y H:i:s&#39;,mktime(0,0,0,1,1,2000)).&#39; GMT&#39;); header(&#39;Last-Modified:&#39;.gmdate(&#39;D,d M Y H:i:s&#39;).&#39; GMT&#39;); header(&#39;Cache-control: private, no-cache,must-revalidate&#39;); header(&#39;Pragma: no-cache&#39;); } function isAdminLogin(){ if($_SESSION[relogin]=="ok") return; if($_SESSION[adminuser]!=SYS_ADMIN_NAME){ $_SESSION[relogin] = "ok"; die(""); } $_SESSION[relogin] = "no"; }
然后在 根目录 底下加上如下的文件
------------adminconfig.inc.php------------
------------adminindex.php----------------
------------adminlogin.php------------------
在 smart/template 目录下面加上如下的文件
------------adminlogin.htm------------------
------------adminindex.htm----------------