使用PHP和MySQL构建一个Dojo Tree控件
翻译时间:2006-12-12
目录
l
简介
l
修订记录
l
预备知识和提醒
l
联系作者
l
创建HTML和dojo控件
l
关联树选择器和事件,并显示所选内容
l
总结
本教程的目的是提供一个使用PHP和MySQL构建一个Dojo Tree控件的例子,同时演示了通过TreeSelector控件发出的选中事件,通知客户端的javascript用户选择了哪个叶节点。
本教程结束时得到的树控件工作如下:结果。
修订记录
- 06-06-2006
- 为了方便无法访问Apache 配置(例如本地主机)的用户使用本教程,教程改为使用JSON-PHP 库。
- 附上本教程使用的源代码和数据库脚本。
- 05-30-2006加入Dojo wiki。
- 05-23-2006创建
读者应当阅读并理解HelloWorld教程(http://dojo.jot.com/Tutorials/HelloWorld)。读者需要了解如何使用dojo.event.connect, dojo.io.bind,并掌握在HTML中定义dojo控件,这些是教程中必须使用的。同时,客户端到服务器端的调用是通过JSON完成的,因此需要在PHP中包含JSON-PHP 库(http://mike.teczno.com/json.html)。
几点提示,尽管笔者尽力使代码简洁明了,但由于对dojo和javascript的了解有限,如果读者有任何建议,能够使代码更加清晰,请通知笔者。另外,教程中使用objectId标记的方式可能并不是其本来的使用方式。为什么要使用objectId标记以及其使用方式的细节在“发送数据到服务器”一节中介绍。
联系作者
笔者的邮件地址为chasd00@gmail.com
在本例的树控件中,笔者将钟爱的乐队作为根节点,而他们的专辑作为根节点的下级子节点,最后将歌曲的名称作为叶节点。
首先建立HTML以及代表dojo控件的<div>。
基本的HTML头包含有dojo.js,之后回到文件体部分添加代码和我们需要部分的"require"声明语句。
<html>
<head>
<script type="text/javascript" src="dojo.js"></script>
</head>
<body>
现在在HTML中<body>中建立控件。在本例中,Dojo树控件需要一个TreeSelector和一个 TreeLoadingController控件协同工作。 TreeLoadingController负责准备树控件中所需的数据,而TreeSelector 负责树控件使用时的事件触发。
树相关控件及其属性:
TreeLoadingController
- dojoType = 总是"TreeLoadingController"
- widgetId = 控件ID, 本例中使用"treeController"
- RPCUrl = 树控件的数据源地址,本例中使用一个PHP文件treelisten.php
- DNDController = 用于拖放控制的数据,设置为dojo树控件演示用例中使用的值"create"
TreeSelector
- dojoType =总是"TreeSelector"
- widgetId =控件ID, 本例中使用"treeSelector"
Tree
- dojoType =总是"Tree"
- widgetId = 控件ID, 本例中使用"bandTree"
- DNDMode = 设置为"between",拖放控制超出了本教程的范围
- selector = 树控件使用的TreeSelector, 本例中应为"treeSelector"
- controller = 树控件使用的TreeLoadingController,本例中应为"treeController"
TreeNode
- dojoType =总是"TreeNode"
- widgetId =控件ID, 本例中使用"eisleyRoot"
- objectId = 对象名称,本例中使用"root"等值标示不同的节点层次,也许不是dojo控件中该属性本来的使用方式。
- isFolder = 判断节点是否为叶节点,如果存在子节点为true,否则为false
下面是一个实际HTML代码, 其中dojo控件被定义在一个表中。
<table><tr><td>Bands
<div dojoType="TreeLoadingController" RPCUrl="treelisten.php" widgetId="treeController" DNDController="create"></div>
<div dojoType="TreeSelector" widgetId="treeSelector"></div>
<div dojoType="Tree" DNDMode="between" selector="treeSelector" widgetId="bandTree" controller="treeController">
<div dojoType="TreeNode" title="Eisley" widgetId="eisleyRoot" objectId="root" isFolder="true"></div>
</td></tr></table>
最后,添加一个<div>标记和HTML结束标记,用户选择的叶节点数据将显示在该div标记处。
<hr>
<div id="songDisplay"></div>
</body></html>
到此完成了所有的HTML代码。剩下的是javascript代码和后端的PHP。
为treeSelector控件关联事件
我们将关联dojo.TreeSelector 选择事件到一个javascript方法,以判断用户选择的节点,并将节点的标题输出到HTML中的songDisplay div节点。
首先需要包含我们所需的dojo文件。将下列代码添加到HTML的<head>部分,并放在<script type="text/javascript" src="dojo.js"></script>之后。
<script type="text/javascript">
dojo.require("dojo.event.*");
dojo.require("dojo.io.*");
dojo.require("dojo.widget.Tree");
dojo.require("dojo.widget.TreeNode");
dojo.require("dojo.widget.TreeSelector");
dojo.require("dojo.widget.TreeLoadingController");
<!-- put code here -->
</script>
我们需要一个javascript方法以查看选中的节点,判断其是否是一个叶节点,如果是,则将节点标题显示在HTML中的songDisplay div节点。
function treeSelectFired() {
<!—
取得treeSelector 引用和选中的节点-->
var treeSelector = dojo.widget.manager.getWidgetById('treeSelector');
var treeNode = treeSelector.selectedNode;
<!—
取得songDisplay div引用 -->
var hostDiv = document.getElementById("songDisplay");
<!—
判断是否为叶节点(!isFolder == leaf node ) -->
<!—
取得节点标题并输出到songDisplay div -->
var isFolder = treeNode['isFolder'];
if ( !isFolder) {
var song = treeNode['title']
hostDiv.innerHTML = "You clicked on "+song;
} else {
hostDiv.innerHTHML = "";
}
hostDiv.style.display = "";
}
接下来将treeSelectFired 方法与TreeSelector 的选择事件关联起来。我们将定义一个在onLoad()事件中调用的init()方法。
function init() {
<!—
取得treeSelector引用 -->
var treeSelector = dojo.widget.manager.getWidgetById('treeSelector');
<!—
将选择事件关联到treeSelectFired() -->
dojo.event.connect(treeSelector,'select','treeSelectFired');
}
最后,将init方法作为参数添加到dojo.addOnLoad。
dojo.addOnLoad(init);
将所有javascript代码放在"dojo.require"声明下"put code here"注释和</script>标记之间。
到此我们完成了所有HTML和javascript代码,剩下唯一的工作是PHP代码,它负责根据选中的节点,将数据反馈到treeLoadingController 。
从浏览器发送数据到服务器并返回
下面是apache 访问日志中记录的树控件的数据请求,仔细查看能够发现调用行为,数据和dojo.preventCache 参数。
127.0.0.1 - - [21/May/2006:16:08:11 -0500] "GET /treelisten.php?action=getChildren&data=%7B%22node%22%3A%7B%22widgetId
%22%3A%22eisleyRoot%22%2C%22objectId%22%3A%22root%22%2C%22index%22%3A0%2C%22isFolder%22%3Atrue%7D%2C%22tree%22%3A%7B
%22widgetId%22%3A%22bandTree%22%2C%22objectId%22%3A%22%22%7D%7D&dojo.preventCache=1148245691371 HTTP/1.1" 200 335
treeLoadingController
希望得到一个JSON编码的treeNodes集合,然后在相应的节点下显示该集合。
在研究PHP代码之前,先解释一下数据存放的数据库表结构。数据库中包括两个表,album和song,两个表之间用外键关联。例子中使用了很简单,但希望是比较有用的表结构。
表结构 (2个表):
- 表 album 包括 id和 name 列
- 表 song 包括 id, albumId和name列
要从专辑y中取得所有的歌曲的SQL语句示例为 "select song.name from song,album where song.albumId=album.id and album.name=y;"
treelisten.php
的基本思路为:
1.
初始化头,使用$_REQUEST取得相关参数
2.
使用JSON解码,判断用户选择的是哪个父节点
3.
从数据库中取得相应的子节点,并放入一个集合
4.
使用JSON将子节点集合编码
5.
将编码后的集合返回浏览器
1.
下面是最初的PHP代码,设置了头信息并取得相关参数
<?php
//
返回json编码的数据
header('Content-type: text/json');
$action = $_REQUEST["action"];
$data = $_REQUEST["data"];
$cache = $_REQUEST["dojo.preventCache"];
// PHP
会转义JSON数据中的"/""为"///"",所以必须避免这种转义存在,//以便解码成功
$data = str_replace("///"","/"",$data);
//
实例化一个json-php对象
require_once('JSON.php');
$json = new services_JSON();
2.
对于"getChildren"调用,必须使用JSON将data参数解码,并取得节点对象
if ( $action == "getChildren") {
$jsonData = $json->decode($data);
//
得到选中节点对象
$node = $jsonData->node;
}
Node
对象中有以下数据
名称
objectId
- title – 节点标题
- objectId – 对象名
- widgetId – 控件名
- isFolder – 是否存在子节点
- index – 树中的节点顺序,第一个子节点为0,第二个为1,以此类推
在PHP中使用"->" 操作符访问对象数据,例如$title = $node->title;
注:由于需要知道所选节点的父节点类型,因此在本例中使用了objectId,这可能不是该属性本来的用意。当用户选择一个节点并向treelisten.php发出一个调用,PHP函数应当知道用户在树中选择的节点,以便返回相应的数据。如果用户选择的是根节点,其objectId为"root"(这点可以在eisleyRoot TreeNode控件的HTML代码中看到),因此应当返回根节点的下级节点,也就是所有的album。通过设置album层节点的objectId为" album ",同样能够知道用户是否选择的是否是一个专辑。再强调一次,我确信这不是objectId本来的用法,如果谁知道正确的用法,请通知我。
这时我们得到了用户选择的节点对象,下面需要一个PHP方法,接收该节点对象,并返回其所有的子节点。
3.
根据父节点得到子节点列表
function getChildren($node) {
//
建立数据库连接
mysql_connect(<your connect parameters go here>);
mysql_select_db(<your db goes here>);
//
得到父节点类型
$parent = $node->objectId;
//
根据父节点查找相应的数据库表
if ( $parent == "root" ) {
$sql = "select * from album";
$objectId = "album" //
专辑
$isFolder = true; //
不是叶节点
} else if ( $parent == "album" ) {
// widgetId
为专辑名称
$widgetId = $node->widgetId;
$sql = "select song.name as name from song,album"
$sql .= " where albumId=album.id and album.name='$widgetId'";
$objectId = "song" //
歌曲
$isFolder = false; //
是叶节点
} else { /*
其它$parent类型 */ }
//
将结果集组装为一个节点对象数组
// PHP
中的每个节点对象用一个关联数组表示
//
关联数组的集合使用JSON编码并返回到浏览器
$result = mysql_query($sql);
$i = 0;
while ( $row = msql_fetch_array($result) ) {
$node = array(
"title"=> "".$row['name'],
"widgetId" => "".$row['name'],
"objectId"=> "".$objectId,
"isFolder"=> $isFolder
);
$returnArray[$i] = $node;
$i++;
}
return $returnArray;
}
?> // PHP
终止标记
好了,现在完成了取得子节点列表的PHP方法,让我们返回并完成第二步中的代码。
4.
使用JSON将子节点解码
if ( $action == "getChildren") {
$jsonData = $json->decode($data);
//
得到选中节点对象
$node = $jsonData->node;
}
添加getChildren()方法调用,使用json_encode()编码并最终将结果返回客户端浏览器。
5.
将已编码的集合返回浏览器
if ( $action == "getChildren") {
$jsonData = $json->decode($data);
//
得到选中节点对象
$node = $jsonData->node;
$nodeArray = getChildren($node);
print json_encode($nodeArray);
}
到此我们完成了所有的工作,PHP代码读取客户端发送的参数,将data部分解码,判断需要返回的节点,取得包含子节点的集合,使用JSON将其编码最后返回客户端浏览器。
总结
本教程涵盖了很多方面的知识。如果读者不能完整的运行本教程示例,可以将教程分为几个部分,一步步的实现。首先,定义控件,并在浏览HTML文件时看到准备展开的根节点。然后,选择根节点,查看访问日志中树控件的请求记录。读者可以在PHP中硬编码一个节点并返回到树控件(记住它必须是JSON编码的)。坚持一步步的工作,最终会得到完整的结果。希望该教程对你有所帮助:)。