UML软件工程组织

 

 

Flex中的MySQL管理
 
2008-03-28 出处:CSDN
 

学习使用RIA Framework Flex创建MySQL管理UI

PHPMyAdmin的出现震撼了业界,这毫无疑问。它当然是基于PHP的最佳应用程序,因为它将MySQL管理界面由命令行的形式改为了web浏览器的形式。不过,虽然它的功能很强大,但使用并不太方便,界面也不够美观。因此,我尝试通过Rich Internet Application框架设计更理想的MySQL前台管理程序。

要达成此目标本可选用Ajax。但我不想处理客户端的不兼容问题。当然,Silverlight也是不错的选择,但它仍不够成熟。 之所以选择Adobe Flex,是因为它拥有富用户接口工具集和方便的web服务集成功能,而且它生成的Flash应用程序能够以相同方式在任何操作系统中运行。

我学习了很多有关创建应用程序方面的知识:如何为PHP程序创建安全的SQL web服务;如何通过Flex访问web服务;如何将web服务返回的数据输入数据网格中并显示。在本文中,我将引领读者从前台到后台,逐步创建MySQL管理程序。读者从中可了解一些有用的信息,以创建自己的Rich Internet应用程序。

创建后台程序

Flex应用程序擅长与web服务通讯,以发出请求及提交数据。因此,首先需要创建一个非常简单的PHP脚本,它将以XML格式返回数据库列表、表或表中的数据。

清单 1. req.php
<?php
require_once("MDB2.php");
$sql = 'SHOW DATABASES';
if ( $_REQUEST['mode'] == 'getTables' )
$sql = 'SHOW TABLES';
if ( $_REQUEST['mode'] == 'getData' )
$sql = 'SELECT * FROM '.$_REQUEST['table'];
$dsn = 'mysql://root@localhost/'.$_REQUEST['db'];
$mdb2 =& MDB2::factory($dsn);
if (PEAR::isError($mdb2)) { die($mdb2->getMessage()); }
$dom = new DomDocument();
$dom->formatOutput = true;
$root = $dom->createElement( "records" );
$dom->appendChild( $root );
$res =& $mdb2->query( $sql );
if (PEAR::isError($mdb2)) { die($mdb2->getMessage()); }
while ($row = $res->fetchRow(MDB2_FETCHMODE_ASSOC))
{
$rec = $dom->createElement( "record" );
$root->appendChild( $rec );
foreach( array_keys( $row ) as $key ) {
$key_elem = $dom->createElement( $key );
$rec->appendChild( $key_elem );
$key_elem->appendChild( $dom->createTextNode( $row[$key] ) );
}
}
$res->free();
$mdb2->disconnect();
header( "Content-type: text/xml" );
echo $dom->saveXML();
?>

该脚本的第一项工作就是利用MDB2库连接数据库。如果没有安装MDB2库,则可使用PEAR安装该库,如下所示:
% pear install MDB2
%

如果PEAR无法正常运行,可访问http://pear.php.net/mdb2,然后下载源代码并将其解包到PHP的include路径下。MDB2是通用的数据库适配器层,它已取代了广为使用的PEAR DB库。

脚本的第二项工作就是创建XML DOM Document对象,该对象将用来创建要输出的XML树。从此处开始,它将运行查询,并在XML树中添加row和column作为XML标签。最后,该脚本将关闭所有连接,并将XML保存到PHP输出流中。

选用XML DOM对象的原因是,它可避免任何与数据、不对称标签等有关的编码问题以及各种可能使XML产生混乱的因素。我可以将调试XML数据流的时间节省下来做其他许多更有意义的工作。您一定也会这样做。

将该脚本安装到本地机器上的可运行目录下,然后使用curl命令向服务器发出请求。

% curl "http://localhost/sql/req.php"
<?xml version="1.0"?>
<records>
<record>
<database>addresses</database>
</record>
<record>
<database>ajaxdb</database>
</record>
...
%

在本例中,我并未指定数据库或模式,这会要求脚本返回可用数据库的清单。假如web服务脚本有权执行该任务,则在curl语句后面就会显示执行的结果。在本例中,将以标签的形式显示不同数据库的列表。

该脚本返回的所有数据都带有<records>标签,它包含一组<record>标签。每个<record>标签的名称和内容取决于已执行的SQL语句。在本例中,只返回单列命名数据库。

除了使用curl命令,还可将URL输入浏览器中,然后在加载页面后选择“View Source(查看源文件)”。

在下例中,将连接articles数据库并获取它的表格列表。结果如下:
% curl ".../req.php?mode=getTables&db=articles"
<?xml version="1.0"?>
<records>
<record>
<tables_in_articles>article</tables_in_articles>
</record>
</records>
%

articles数据库中只有一个名为article的表格,这并不奇怪。要运行经典的select * from article查询以获取所有记录,可使用以下URL:
% curl ".../req.php?mode=getData&db=articles&table=article"
<?xml version="1.0"?>
<records>
<record>
<id>1</id>
<title>Apple releases iPhone</title>
<content>Apple Computer is going to release the iPhone...</content>
</record>
<record>
<id>2</id>
<title>Google release Gears</title>
<content>Google, Inc. of Mountain View California has...</content>
</record>
</records>
%

表格中有两条记录:第一条显示Apple公司将发布超炫的IPhone;第二条显示Google公司将发布同样很炫、但用途完全不同的Gears系统。

在本地机器上安装了极为强大且灵活的后台程序后,就可以着手为其创建Flex前台程序了。

创建用户界面

我想以层的形式创建Flex应用程序,首先是用户界面,然后在框架中添加代码以实现互动性。首先,若尚未安装Flex SDK则先安装。Flex SDK是免费软件,Flex Builder 2 IDE也有免费试用版。可从http://www.flex.org/download下载Flex SDK。Flex SDK是免费的,Flex 3 SDK也将成为开源软件。Flex Builder是一种基于Eclipse的开发环境,通过拖放控件即可创建界面,但本例不必使用该工具。我非常喜欢使用标签,因此通常采用代码视图并直接修改MXML。

我想在界面顶部放置几个控件并在底部放置一个数据网格,以显示表中的数据。顶部的控件就是两个下拉列表,可在其中选择数据库和表。

该窗体的MXML如清单2所示。

清单 2. flexmysql1.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
<mx:VBox horizontalAlign="left">
<mx:HBox>
<mx:Label text="Database:" />
<mx:ComboBox id="selectedDatabase" width="381" height="21">
</mx:ComboBox>
</mx:HBox>
<mx:HBox>
<mx:Label text="Table:" />
<mx:ComboBox id="selectedTable" width="381" height="21">
</mx:ComboBox>
</mx:HBox>
<mx:DataGrid id="dg1" width="452">
</mx:DataGrid>
</mx:VBox>
</mx:Application>

在Flex Builder 2中编译及运行这段代码时,结果如图1所示。

图1.界面布局

相当整洁,对吧?渐变背景很漂亮,控件也很美观。我只做了极少量的工作。当然,图形设计师也可添加一些修饰,以使其更加美观,例如过渡效果和图像。而我还是喜欢整洁的界面,并要确保它在每台客户机上都具有一致的外观和表现,而Ajax应用程序要做到这一点并不容易。

获取数据库清单

补充Flex应用程序代码的第一步就是,当应用程序加载后在窗体顶部显示数据库的组合框。相关代码如清单3所示。

清单 3. flexmysql2.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
initialize="onInitialize()">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
private static const SERVICE_BASE:String = "http://localhost/sql/req.php";
public function onInitialize():void
{
myservice.url = SERVICE_BASE;
myservice.send( null );
} public function onResult(event:Event):void
{
selectedDatabase.dataProvider = myservice.lastResult..database.*;
}
]]>
</mx:Script>
<mx:HTTPService id="myservice" result="onResult(event)" resultFormat="e4x">
</mx:HTTPService>
<mx:VBox horizontalAlign="left">
<mx:HBox>
<mx:Label text="Database:" />
<mx:ComboBox id="selectedDatabase" width="381" height="21">
</mx:ComboBox>
</mx:HBox>
<mx:HBox>
<mx:Label text="Table:" />
<mx:ComboBox id="selectedTable" width="381" height="21">
</mx:ComboBox>
</mx:HBox>
<mx:DataGrid id="dg1" width="452">
</mx:DataGrid>
</mx:VBox>
</mx:Application>

我省略了与前例类似的部分代码,以缩短代码段长度。差别重点在于<mx:HTTPService>标签,它将创建HTTP Service Flex对象,而后者将与服务器进行通讯。该服务对象由onInitialize方法调用, 而后者在Flex应用程序启动时调用。onInitialize方法先设置服务的URL,然后开始发出请求。请求完成后,就调用onResult方法。onResult方法将组合框的dataProvider设为web服务器的返回结果。

该方法最有价值的部分如下: myservice.lastResult..database.*

看看这个语句。如果之前不了解E4X的作用,我可以稍加解释。myservice.lastResult变量实际上是一个XML文档。‘..’句法等价于XPath ‘//’句法。它表示“将具有此名字的任何一个标签给我”,在本例中就是“将任何一个数据库标签给我”。星号表示数据库标签的任何子标签。由于数据库标签只有一个子标签,即数据库名称的文字部分,因此这段代码就表示“将XML文档中每个数据库的名称给我”。这个功能真的很好用!

正是由于ActionScript的这种E4X扩展,Flex才能够方便地与XML数据源进行通讯。据我所知,还没有其他语言能够如此方便地查询XML文档。因此利用强大的E4X,就可以在浏览器中运行该应用程序,并得到如图2所示的结果。

图2.填充数据库名称的数据库组合框

单击该组合框时,将弹出下拉列表,并显示机器中的数据库列表。是的,其中有很多数据库。几乎每篇有关PHP、Flex、Rails或其他技术的文章都会用到数据库,所以我拥有大量的数据库。

创建表视图代码

创建示例Flex应用程序的最后一步就是添加表格的下拉菜单,并在数据网格中显示选中表格的数据。该例的完整代码如清单4所示。

清单 4. flexmysql.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
initialize="onInitialize()">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
private static const SERVICE_BASE:String = "http://localhost/sql/req.php";
private var loadingDatabases:Boolean = true;
private var loadingTables:Boolean = false;
public function onInitialize():void
{
loadingDatabases = true;
myservice.url = SERVICE_BASE;
myservice.send( null );
} public function onResult(event:Event):void
{
if ( loadingDatabases )
{
loadingDatabases = false;
selectedDatabase.dataProvider = myservice.lastResult..database.*;
onSelectDatabase();
}
else if ( loadingTables )
{
loadingTables = false;
var tables:Array = new Array();
for each ( var tablRecord:XML in myservice.lastResult..record )
{
for each( var tablCol:XML in tablRecord.* )
tables.push( tablCol..*.toString() );
}
selectedTable.dataProvider = tables;
onSelectTable();
} else
{
var records:Array = new Array();
for each ( var record:XML in myservice.lastResult..record )
{
var outRecord:Array = new Array();
for each( var column:XML in record.* )
outRecord[ column.name() ] = column..*.toString();
records.push( outRecord );
}
var data:ArrayCollection = new ArrayCollection( records );
dg1.dataProvider = data;
}
} public function onSelectDatabase():void
{
loadingDatabases = false;
loadingTables = true;
var url:String = SERVICE_BASE;
url += "?mode=getTables&db="+selectedDatabase.selectedLabel;
myservice.url = url;
myservice.send(null);
} public function onSelectTable():void
{
var url:String = SERVICE_BASE;
url += "?mode=getData&db="+selectedDatabase.selectedLabel;
url += "&table="+selectedTable.selectedLabel;
myservice.url = url;
myservice.send(null);
}
]]>
</mx:Script>
<mx:HTTPService id="myservice" result="onResult(event)" resultFormat="e4x">
</mx:HTTPService>
<mx:VBox horizontalAlign="left">
<mx:HBox>
<mx:Label text="Database:" />
<mx:ComboBox id="selectedDatabase" width="381" height="21"
change="onSelectDatabase()">
</mx:ComboBox>
</mx:HBox>
<mx:HBox>
<mx:Label text="Table:" />
<mx:ComboBox id="selectedTable" width="381" height="21"
change="onSelectTable()">
</mx:ComboBox>
</mx:HBox>
<mx:DataGrid id="dg1" width="452">
</mx:DataGrid>
</mx:VBox>
</mx:Application>

其中有一处重要修改,即添加了onSelectDatabase()函数的代码,以获取表格列表,然后将其输入到onResult()函数中,还添加了onSelectTable()函数,该函数可检索表格中的数据,并在onResult()处理器中设置数据网格。

在浏览器中运行该例时,结果如图3所示。

图3. 运行“show tables”查询之后的结果

本例中使用了nasa数据库,这是我为NASA Ames演讲而创建的数据库(我用了“火箭科学”的笑话,因为当时我的听众是一群火箭科学家)。该数据库只有一个名为mission的表,其中包含两列,分别代表每项由NASA发起但已撤消的任务名称和年份。

在数据网格中单击名称列即可按名称排序,如图4所示。

图 4. 显示mission表中的数据

然后单击年份列,以按照年份排列数据,如图5所示。

图 5. 迪斯科球式查询

在过去几年中,我们似乎并未发起任何任务。

后续步骤

至此,本例已可应用于许多场合。在前台Flex应用中,可以添加一些对用户更友好的控件,以添加、删除或更新表格。甚至可以提供数据过滤功能或者编辑功能,以修改表格中的数据。数据网格控件非常强大,可以处理现场编辑任务。也可以用在完全不同的场合,例如使用Flex绘图软件包执行数据分析和可视化工作。

总结

PHP和Flex的结合潜力无限。利用PHP可以方便地在后台发布web服务,可以单独发布,可以与HTML前台程序一起发布。PHPMyAdmin带有web服务接口扩展功能,使任何人都能以Flex、Ajax、Silverlight、桌面小部件或其他任何技术编写前台程序。Flex为PHP开发人员提供了强大的前台工具。它避开了困扰Ajax开发人员的客户端不兼容问题。Flex 2并非只能使用特定的服务器技术,因此Flex应用程序编译完成后,它就可以部署在任何场合,因为它只是一个SWF文件。可以看到,利用简单的web服务对象和强大的E4X语言扩展,就可以让Flex应用程序方便地与几乎任何一种后台程序进行通讯。

读者不妨一试,如果您使用PHP和Flex创建了有趣的程序,请告诉我,也许我们可以合写一篇文章来介绍它。

资源

  • Flex是一种开源Flash应用程序开发语言,由Adobe创建。
  • MDB2 PEAR模块是我用来访问数据库的方法。
 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号