MVC只是一种设计模式而已,一度被认为Model 1,也就是服务器语句与HTML语句杂糅的php,其实不用任何框架,仅仅利用原生态的JavaScript Ajax也可以对其进行MVC设计。由于什么都没有用,因此对IE6的兼容性是非常强的。还是《【php】数据库的增删改查和php与javascript之间的交互》(点击打开链接)那个页面的,对数据库增删改查的内容,希望各位能推广到整个网站。
一、基本目标
整个网页实现效果如下,用户输入完表单马上就有效果。
二、基本思想
首先,在test数据库中有一张这样的用户信息表,建表的时候注意检查一下那些数据库字段的编码是否是utf-8,一些Mysql在安装时候没有改默认的编码latin1的,不然一会儿你打死无法存中文。
然后,整个网页工程结构如下图:
在view.php中网页中不进行任何刷新,利用原生态的JavaScript Ajax,直接完成V-C层的交互。C-M层的交互通过include语句,引入M层的业务逻辑类中方法,通过类参数的传递完成。
同时,C、M层的php拒绝直接输入网址访问。这里不像JSP或者ASP,C层、M层都是编译之后的JAVA文件或者C#文件,根本就访问不了。我们还要对其进行保护。
三、制作过程
1、View层就一个简单的View.php,其布局如下,没什么好说的,非常简单的HTML布局。同时,注意,本页面:
(1)没有设置表单,所有表单提交的动作,通过button触发相应的JavaScript而触发。
(2)各个组件的ID,一会儿在JavaScript用到。其中用户信息表、修改ID的下拉框都是通过下面的脚本Ajax而加载的。
(3)布局中,包括一会儿的脚本在内没有任何php代码,便于各位布局。你把后缀名改成.html也能够正常运行。什么叫做真正的View层?这就是了,没有任何的服务器代码。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>用户信息表</title> </head> <body> 用户信息表如下:<div id="result"></div> <hr /> 插入数据<br /> 用户名:<input type="text" name="username" /> 密码:<input type="text" name="password" /> <button onclick="insert()">GO!</button> <hr /> 修改数据<br /> <select id="userid" name="userid"></select> <select name="rowname"> <option value="username">用户名</option> <option value="password">密码</option> </select> <input type="text" name="rowtext" /> <button onclick="update()">GO!</button> </body> </html>
其实整个View层的精髓在于下面的JavaScript脚本。View层就无须使用到Xajax这个插件了,《【php】Xajax Helloworld》(点击打开链接)。直接用原生态的JavaScript写。虽然代码量比起JQuery等前端框架较多,记住不容易。从建立Ajax对象,设置Ajax请求头,处理Ajax文本都要自己写,但在开发过程中,只是复制粘贴而已。因此可以观察到,所有的Ajax交互函数都大同小易。拿ForAllUserInfo();这个函数来重点说明怎么V-C层怎么通过Ajax交互。
<script> //首先,本页面一加载就调用两个函数,加载本页。 ForAllUserInfo(); ForTotal(); //创建Ajax对象,不同浏览器有不同的创建方法,其实本函数就是一个简单的new语句而已。 function createXMLHttpRequest(XMLHttpRequest){ var XMLHttpRequest; if(window.XMLHttpRequest){ XMLHttpRequest=new XMLHttpRequest(); } else if(window.ActiveXObject){ try{ XMLHttpRequest=new ActiveXObject("Msxml2.XMLHTTP"); } catch(e){ XMLHttpRequest=new ActiveXObject("Microsoft.XMLHTTP"); } } return XMLHttpRequest; } function ForAllUserInfo(){ var XMLHttpRequest=createXMLHttpRequest(XMLHttpRequest); //指明相应页面 var url="dbselect.php"; XMLHttpRequest.open("POST",url,true); //这里没法解释,你所有JavaScript的请求头都这样写就对了,不会乱码 XMLHttpRequest.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); //对于dbselect.php,本view.php没有任何参数给你。 XMLHttpRequest.send(null); //对于返回结果怎么处理的问题 XMLHttpRequest.onreadystatechange=function(){ //这个4代表已经发送完毕之后 if(XMLHttpRequest.readyState==4){ //200代表正确收到了返回结果 if(XMLHttpRequest.status==200){ //那么id为result的div,就是整个dbselect.php页面了。 document.getElementById("result").innerHTML=XMLHttpRequest.responseText; } else{ //如果不能正常接受结果,你肯定是断网,或者我的服务器关掉了。 alert("网络连接中断!"); } } }; } function ForTotal(){ var XMLHttpRequest=createXMLHttpRequest(XMLHttpRequest); var url="dbtotal.php"; XMLHttpRequest.open("POST",url,true); XMLHttpRequest.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); XMLHttpRequest.send(null); XMLHttpRequest.onreadystatechange=function(){ if(XMLHttpRequest.readyState==4){ if(XMLHttpRequest.status==200){ var total=parseInt(XMLHttpRequest.responseText); //先把修改ID的下拉列表清空 document.getElementById("userid").innerHTML=""; if(total>0){ for(var i=1;i<total+1;i++){ //javascript增加节点过程,数据库有多少项就填充多少个ID给用户修改。 var selectnode=document.createElement("option"); selectnode.value=i; selectnode.innerHTML=i; document.getElementById("userid").appendChild(selectnode); } } } else{ alert("网络连接中断!"); } } }; } function insert(){ //从输入框拿来插入数据的所有表单 var username=document.getElementById("username").value; var password=document.getElementById("password").value; //如果用户输入的值不为空,则执行Ajax if(username!=""&&password!=""){ var XMLHttpRequest=createXMLHttpRequest(XMLHttpRequest); var url="dbinsert.php"; XMLHttpRequest.open("POST",url,true); XMLHttpRequest.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); //这里View层的View.php需要发送两个参数给Controll层的dbinsert.php处理 XMLHttpRequest.send("username="+username+"&password="+password); XMLHttpRequest.onreadystatechange=function(){ if(XMLHttpRequest.readyState==4){ if(XMLHttpRequest.status==200){ //添加数据成功,则部分刷新View层的用户信息表与修改ID的下拉列表 ForAllUserInfo(); ForTotal(); } else{ alert("网络连接中断!"); } } }; } else{ alert("不得为空!"); } } //这里修改数据与insert()同理 function update(){ var userid=document.getElementById("userid").value; var rowname=document.getElementById("rowname").value; var rowtext=document.getElementById("rowtext").value; if(userid!=""&&rowname!=""&&rowtext!=""){ var XMLHttpRequest=createXMLHttpRequest(XMLHttpRequest); var url="dbupdate.php"; XMLHttpRequest.open("POST",url,true); XMLHttpRequest.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); XMLHttpRequest.send("userid="+userid+"&rowname="+rowname+"&rowtext="+rowtext); XMLHttpRequest.onreadystatechange=function(){ if(XMLHttpRequest.readyState==4){ if(XMLHttpRequest.status==200){ ForAllUserInfo(); ForTotal(); } else{ alert("网络连接中断!"); } } }; } else{ alert("不得为空!"); } } </script>
2、Controll层
Controll层的页面都与View层中的JavaScript函数一一对应。
比如dbupdate.php就对应view.php中的update()函数。通过如下两句对应起来。
var url="dbupdate.php"; XMLHttpRequest.open("POST",url,true);
(1)dbupdate.php
非常简单的页面,拿到前端送过来的两个数据,则进行与Model层的交互,完成数据库的操作。修改数据库,数据库是不会返回任何结果的,因此也没有什么数据给前端的。如果拿不到前端送过来的参数,那一定是恶意用户,通过输入网址非正常打开此页。C层M层一般是不给你打开的。
<?php include_once("db.php"); if(empty($_REQUEST["userid"])){ header("Content-type: text/html; charset=utf-8"); echo "请正常打开此页!"; } else{ $db=new db(); $userid=$_REQUEST["userid"]; $rowname=$_REQUEST["rowname"]; $rowtext=$_REQUEST["rowtext"]; $db->modify("update user set ".$rowname."='".$rowtext."' where id=".$userid.";"); } ?>
(2)dbinsert.php
插入数据同样的道理了,不再赘述。对应于前端的insert()函数
<?php include_once("db.php"); if(empty($_REQUEST["username"])){ header("Content-type: text/html; charset=utf-8"); echo "请正常打开此页!"; } else{ $db=new db(); $username=$_REQUEST["username"]; $password=$_REQUEST["password"]; $db->modify("insert into user(username,password) values ('".$username."','".$password."');"); } ?>
(3)dbtotal.php
这一页是用来查询数据库有多少条结果,我们在修改数据的下拉列表就要提供给用户多少个ID,给用户指定修改。
对应于view.php中的ForTotal()函数。这一页是有返回结果的。因此dbtotal.php就把这个结果用echo打印出来,前端通过:
var total=parseInt(XMLHttpRequest.responseText);
这一句中的XMLHttpRequest.responseText拿到,前端的JavaScript必须强制指明这是数字,否则则出现7+1=71的神运算。JavaScript把数字当字符串了,也没办法了,毕竟所有变量都是var。php则都是美元$。
这一页就无须保护了,毕竟肯定要给用户看的。
<?php include_once("db.php"); $db=new db(); $total=$db->getTotal(); echo $total; ?>
(4)dbselect.php
这一页其实和dbtotal.php一样,不过变成了构造一个表格,送给前端View.php的ForAllUserInfo()。ForAllUserInfo()得到的数据其实一段HTML文本,直接通过.innerHTML放上去就可以了。
<?php include_once("db.php"); $db=new db(); $user=$db->getAllUserInfo(); ?> <table border="1"> <tr><th>ID</th><th>用户名</th><th>密码</th></tr> <?php for($i=0;$i<count($user);$i++){ echo "<tr><td>".$user[$i]['id']."</td><td>".$user[$i]['username']."</td><td>".$user[$i]['password']."</td></tr>"; } ?> </table>
这页有HTML打死都不能与PHP代码混在一起的强迫症患者,请自行把所有HTML的代码,给成echo输出,反正我就只能给出这样的一个方案了。
3、Model层
这一层的所有方法都与C层的页面存在对应关系。
首先都公用一个数据库连接函数。之后各自在方法中调用,最后各自查询完毕则关闭这个连接。
然后,可以注意到上面的Controll层的dbupdate.php与dbinsert.php公用此类的一个方法。这主要是考虑到,都是传递一个SQL语句过来,然后没有返回结果,因此可以合在一起了。而查询数据库的所有数据与查询数据库的数据数量的返回结果是不同的,因此分开两个方法。
<?php function createCon(){ //数据库的地址是localhost:3306,数据库用户名(第二项)是root,数据库密码(第三项)是root $con=mysql_connect("localhost","root","root"); if(!$con){ die("连接失败!"); } //要操作test数据库 mysql_select_db("test",$con); //防止乱码 mysql_query("set names utf8;"); return $con; } class db{ public function getAllUserInfo(){ $con=createCon(); $result=mysql_query("select * from user;"); //如果查询的结果多,就放到一个二维数组里面,返回给Controll层 //Controll层再对这个二维数组一一处理。 //起始这个二维数组不就相当于JSP中的ArrayList吗?^_^ $userList=array(); for($i=0;$row=mysql_fetch_array($result);$i++){ $userList[$i]['id']=$row['id']; $userList[$i]['username']=$row['username']; $userList[$i]['password']=$row['password']; } mysql_close($con); return $userList; } public function getTotal(){ $con=createCon(); $result=mysql_query("select count(*) as total from user;"); //如果返回结果只有一个,那就直接这样取数据。 $row=mysql_fetch_array($result); mysql_close($con); return $row['total']; } public function modify($sql){ //对于那些传sql过来没有返回结果的方法,归纳到同一类 $con=createCon(); mysql_query($sql); mysql_close($con); } } ?>
四、总结与展望
上面的制作过程最好合在一起看,反正我只能这样分层贴了。V-C,C-M一直在交互,从未被割裂,根本停不下来。对比与《【php】数据库的增删改查和php与javascript之间的交互》(点击打开链接)这个以MODEL1模式创作的工程,页面虽然增多,但是模块更加地清晰。
反正MVC仅仅是一种设计模式、设计思想而已,在PHP同样也能够实现。在JSP对这种模式的吹嘘是言过其实了,主要是JSP的部分创作者,不停地对于框架的使用,而忘记了这门语言的本质。
我觉得这个例子,再次证明了语言只是思想表达的载体。无插件无框架,纯HTML+CSS与纯JavaScript加PHP就能够实现,兼容IE6。如果你打包一样,放上防注入函数,完全可以成为自己的框架的。
对比与JSP与ASP,我在写PHP的时候更加舒服,告别了MyEclipse/Eclipse与Visual Studio的卡爆,用着早已被批得一毛不是Dreamwaver,甚至还可以用记事本写着网页,半点不卡。一台垃圾配置的Winxp就能够创造出好的网站。关键是你的语言基本功问题了。少做点喷子,多做点工程。多接触几门编程语言。