<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[沧海一粟]]></title> 
<link>http://www.dzhope.com/index.php</link> 
<description><![CDATA[Web系统架构与服务器运维,php开发]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[沧海一粟]]></copyright>
<item>
<link>http://www.dzhope.com/post//</link>
<title><![CDATA[用 PHPRPC 实现 Ajax 级联下拉菜单]]></title> 
<author>jed &lt;jed521@163.com&gt;</author>
<category><![CDATA[代码编程]]></category>
<pubDate>Thu, 21 Sep 2006 01:48:31 +0000</pubDate> 
<guid>http://www.dzhope.com/post//</guid> 
<description>
<![CDATA[ 
	级联下拉菜单就是从一个下拉菜单中选中一项后，相应的另一个下拉菜单的内容会随之改变。<br/><br/>一般来说，最简单的，就是每次选中都提交一次表单，刷新整个页面。这也是用户体验度最差的。<br/><br/>另一种是把所有选项在第一次加载时就全部载入整个页面中的 JavaScript 数组中，然后级联通过 JavaScript 来控制，在整个数据量不大时，这是一个不错的实现无刷新并且快速的方法，但是当整个数据量非常大时，这种方法就会使第一次加载变得非常慢了。<br/><br/>还有就是采用 Ajax 方式，即开始只载入第一层菜单的内容，当用户选中第一层菜单的某项时，再通过 XmlHttpRequest 来获取相应选项所对应的第二层菜单的内容。这种方式效果最好，但是采用传统方式来编写这样的 Ajax 程序代码量会比较多。而且如果设计不好，服务器端返回菜单内容的程序的可复用性也会很差。<br/><br/>但是在本文中你会看到用 PHPRPC 来实现这种 Ajax 效果是多么的简单，并且还会具有非常高的可复用性。<br/><br/><br/>本文以省市两级级联下拉菜单为例，为了举例方便，本文中采用的是 SQLite 数据库，因为这个文件型数据库比较容易部署，而且查询效率很高（当然创建该数据库的效率不高，但创建仅一次而已，该数据库在该程序中内容是不变的），不过服务器需要安装 SQLite 扩展。<br/><br/>这个数据库中的表只有 2 个，一个 province 表，一个 city 表。province 表中，只有 id 和 name 两个字段，分别是省份编号（主键）和省名。city 表中，有 id、name 和 pid 三个字段，id 是城市编号，name 是城市名，pid 是城市所在省的编号，与 province 表中的省份编号相对应。<br/><br/>创建该数据库的程序这里就不给出来了，它包含在后面提供的实例下载中。<br/><br/>下面来看看创建这个程序的服务器端有多么简单，为了提高可复用性，我们把服务器端分为了 2 个文件，一个是 function.php，另一个是 rpc.php。function.php 中定义了实际的远程调用函数，但是他们也可以作为服务器端的本地函数调用，你会发现他们跟服务器端的普通函数没有任何区别：<br/><br/>下载: function.php<br/>&lt;?php<br/>$sqlite = new SQLiteDatabase(&#039;area.db&#039;);<br/> <br/>function get_province() {<br/> &nbsp; &nbsp;global $sqlite;<br/> &nbsp; &nbsp;$sql = &quot;select * from province order by id&quot;;<br/> &nbsp; &nbsp;return $sqlite-&gt;arrayQuery($sql, SQLITE_ASSOC);<br/>}<br/> <br/>function get_city($pid) {<br/> &nbsp; &nbsp;global $sqlite;<br/> &nbsp; &nbsp;$pid = sqlite_escape_string($pid);<br/> &nbsp; &nbsp;$sql = &quot;select * from city where pid = $pid order by id&quot;;<br/> &nbsp; &nbsp;return $sqlite-&gt;arrayQuery($sql, SQLITE_ASSOC);<br/>}<br/>?&gt;<br/>而 rpc.php 更加简单，它是提供给客户端调用的接口，它只有 3 行语句：<br/><br/>下载: function.php<br/>&lt;?php<br/>require_once(&#039;function.php&#039;);<br/>require_once(&#039;phprpc_server.php&#039;);<br/>new phprpc_server(array(&#039;get_province&#039;, &#039;get_city&#039;));<br/>?&gt;<br/>其中最后一句，就是指定哪些函数要暴露给客户端。只有指定的函数客户端才可以调用，这样可以保证服务器的安全性。<br/><br/>服务器端到此就创建完了。你会发现服务器端只负责把数据查询出来返回给客户端就完事了，其它的不做任何处理。<br/><br/>那么下面该看一看客户端了，客户端虽然很简单，但是我还是把它分成了两个文件，一个 js 文件，一个 html 文件，你会发现用 PHPRPC，客户端都不需要用 PHP。<br/><br/>下载: area.js<br/>// 创建 phprpc 客户端对象 rpc<br/>phprpc_client.create(&#039;rpc&#039;);<br/> <br/>var city = []; // 用于缓存已加载的城市数据<br/> <br/>/* <br/> * 清除 select 中的选项，该方法可复用<br/> *<br/> * so: 要清除选项的 select 对象<br/> *<br/> */<br/>function clear_select(so) {<br/> &nbsp; &nbsp;for (var i = so.options.length - 1; i &gt; -1; i--) {<br/> &nbsp; &nbsp; &nbsp; &nbsp;// 有些浏览器不支持 options 属性的 remove 方法，<br/> &nbsp; &nbsp; &nbsp; &nbsp;// 但支持 DOM 的 removeChild 方法（比如：Konqueror）<br/> &nbsp; &nbsp; &nbsp; &nbsp;if (so.options.remove) {<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;so.options.remove(i);<br/> &nbsp; &nbsp; &nbsp; &nbsp;}<br/> &nbsp; &nbsp; &nbsp; &nbsp;else {<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;so.removeChild(so.options[i]);<br/> &nbsp; &nbsp; &nbsp; &nbsp;}<br/> &nbsp; &nbsp;}<br/>}<br/> <br/>/*<br/> * 设置 select 中的选项，该方法可复用<br/> *<br/> * so: 要设置选项的 select 对象<br/> * &nbsp;d: 选项数据数组<br/> * vf: 选项值所对应的数组中的字段名<br/> * tf: 选项文本所对应的数组中的字段名<br/> */<br/>function set_select(so, d, vf, tf) {<br/> &nbsp; &nbsp;for (var i = 0, n = d.length; i &lt; n; i++) {<br/> &nbsp; &nbsp; &nbsp; &nbsp;var opt = document.createElement(&#039;option&#039;);<br/> &nbsp; &nbsp; &nbsp; &nbsp;opt.text = d[i][tf];<br/> &nbsp; &nbsp; &nbsp; &nbsp;opt.value = d[i][vf];<br/> &nbsp; &nbsp; &nbsp; &nbsp;// 有些浏览器不支持 options 属性的 add 方法，<br/> &nbsp; &nbsp; &nbsp; &nbsp;// 但支持 DOM 的 appendChild 方法（比如：Konqueror）<br/> &nbsp; &nbsp; &nbsp; &nbsp;if (so.options.add) {<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;so.options.add(opt);<br/> &nbsp; &nbsp; &nbsp; &nbsp;}<br/> &nbsp; &nbsp; &nbsp; &nbsp;else {<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;so.appendChild(opt);<br/> &nbsp; &nbsp; &nbsp; &nbsp;}<br/> &nbsp; &nbsp;}<br/>}<br/> <br/>// 设置省份的下拉菜单<br/>function set_province_select(d) {<br/> &nbsp; &nbsp;var so = document.getElementById(&#039;province&#039;);<br/> &nbsp; &nbsp;set_select(so, d, &#039;id&#039;, &#039;name&#039;);<br/> &nbsp; &nbsp;// 设置首选省份的城市下拉列表<br/> &nbsp; &nbsp;change_province(1);<br/>}<br/> <br/>// 设置城市的下拉菜单<br/>function set_city_select(d, vf, tf) {<br/> &nbsp; &nbsp;var so = document.getElementById(&#039;city&#039;);<br/> &nbsp; &nbsp;// 清空原有选项<br/> &nbsp; &nbsp;clear_select(so);<br/> &nbsp; &nbsp;// 设置新选项<br/> &nbsp; &nbsp;set_select(so, d, vf, tf);<br/>}<br/> <br/>// 当省份改变，相应的改变城市列表<br/>function change_province(pid) {<br/> &nbsp; &nbsp;// 如果已缓存，则直接显示缓存中的列表<br/> &nbsp; &nbsp;if (city[pid]) {<br/> &nbsp; &nbsp; &nbsp; &nbsp;set_city_select(city[pid], &#039;id&#039;, &#039;name&#039;);<br/> &nbsp; &nbsp;}<br/> &nbsp; &nbsp;else {<br/> &nbsp; &nbsp; &nbsp; &nbsp;// 否则，先显示载入中<br/> &nbsp; &nbsp; &nbsp; &nbsp;set_city_select([[&#039;&#039;, &#039;Loading...&#039;]], 0, 1);<br/> &nbsp; &nbsp; &nbsp; &nbsp;// 然后调用远程过程载入城市信息<br/> &nbsp; &nbsp; &nbsp; &nbsp;// 调用远程过程时，最后一个参数指定的是回调函数<br/> &nbsp; &nbsp; &nbsp; &nbsp;rpc.get_city(pid, function (result) {<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 把载入的数据放入缓存<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;city[pid] = result;<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 更新城市列表<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;set_city_select(result, &#039;id&#039;, &#039;name&#039;);<br/> &nbsp; &nbsp; &nbsp; &nbsp;});<br/> &nbsp; &nbsp;}<br/>}<br/> <br/>// 定义当 rpc 客户端初始化（use_service）完毕后执行的内容<br/>rpc.onready = function () {<br/> &nbsp; &nbsp;// 调用获取省份内容的远程过程，并设置回调函数为 set_province_select<br/> &nbsp; &nbsp;rpc.get_province(set_province_select);<br/>}<br/>下载: index.html<br/>&lt;html&gt;<br/>&lt;head&gt;<br/>&lt;script type=&quot;text/javascript&quot; src=&quot;phprpc_client.js&quot;&gt;&lt;/script&gt;<br/>&lt;script type=&quot;text/javascript&quot; src=&quot;area.js&quot;&gt;&lt;/script&gt;<br/>&lt;/head&gt;<br/>&lt;body onload=&quot;rpc.use_service(&#039;rpc.php&#039;);&quot;&gt;<br/>&lt;select id=&quot;province&quot; onchange=&quot;change_province(this.value);&quot;&gt;&lt;/select&gt;<br/>&lt;select id=&quot;city&quot;&gt;&lt;/select&gt;<br/>&lt;/body&gt;<br/>&lt;/html&gt;<br/>上面的 html 中包含的 phprpc_client.js 是压缩版本（因为不需要用到加密，这里是 lite 压缩版）的，这样可以免去包含多个 js 文件的麻烦。<br/><br/>大家会发现这个程序不但可复用性好（比如 clear_select 和 set_select 两个函数也可以在其它程序中使用），而且使得整个程序的思路清晰，比如那个缓存功能，在这里实现的就非常的简单，而且效果也非常的好。<br/><br/>演示程序 <br/>实例下载 <br/>通过 PHPRPC，你不需要再专注于服务器端和客户端的数据格式交换，不需要再去考虑 XmlHttpRequest 对象的创建和使用，PHPRPC 会自动帮你完成这一切，你只需要关注具体的事务就可以了。用 PHPRPC 来做 Ajax 编程，就是这么简单。<br/>
]]>
</description>
</item><item>
<link>http://www.dzhope.com/post//#blogcomment</link>
<title><![CDATA[[评论] 用 PHPRPC 实现 Ajax 级联下拉菜单]]></title> 
<author> &lt;user@domain.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate> 
<guid>http://www.dzhope.com/post//#blogcomment</guid> 
<description>
<![CDATA[ 
	
]]>
</description>
</item>
</channel>
</rss>