级别: 中级 Nathan A. Good, 高级信息工程师, Freelance Developer
2009 年 8 月 20 日 Mate 是一个轻量级的事件驱动框架,该框架支持以 Model-View-Controller (MVC) 模式构建用户界面(UI)和服务。本文学习如何联合使用 Eclipse PHP 开发工具(PDT)和 Flex 软件开发工具(SDK)构建使用 Mate 框架的应用程序。本文详细讲解现有的 Mate 文档,因为它关注使用 Eclipse PDT 作为工具。
Adobe® Flash® 是另一种构建富 Internet 应用程序(RIA)的好方法。使用 Eclipse 和 Adobe Flex SDK,您可以将项目编译进 Flash Player 中运行的应用程序。Mate 是一个轻量级的事件驱动框架,它支持通过调用函数轻松调用远程对象。这些远程对象可能是用 PHP 编写的服务。
要运行本文中的示例,您需要 Eclipse PDT。本文还将介绍您需要的其他组件。
Mate 和 Flex SDK 概述
Flex SDK 允许您构建 Adobe Flex 应用程序。Flex 应用程序使用 XML 格式和 ActionScript V3.0 语言的源文件。使用编译器,您可以将 .mxml 文件和 ActionScript 文件编译为 Adobe Flex 应用程序。
可以下载两个版本的 Flex SDK:
- Adobe Free 版包含编译应用程序的 Adobe 产品,还包含 Adobe AIR 和其他组件。Adobe Free SDK 通过 Adobe Flex SDK 许可证许可(参见 参考资料)。
- 另一个 SDK 版本是 Open Source Flex SDK 版,它遵循 Mozilla Public License (MPL) 许可。它包含一个编译器和运行本文中的示例需要的所有组件。但是,MPL 许可可能满足不了您的需要。咨询您的法律部门,验证这个许可是否适用。
另外,Adobe 提供一个称为 Flex Builder 的产品,您可以将其作为一个 Eclipse 插件或者作为一个完整的产品下载。为运行本文中的示例,请下载开源 Flex SDK。下载 ZIP 文件并将其解压缩到一个简单易记的本地位置,本文将这个位置称为 FLEX_HOME。本文将演示如何使用这个免费 SDK 设置 Eclipse,以便您可以只使用 Eclipse、PDT 和 Flex SDK 构建 RIA。
Mate 是一个帮助您构建遵循 MVC 模式的应用程序的框架。Mate 以事件驱动,因此您需要创建一些 ActionScript 类事件。然后将这些事件映射到远程对象调用,以便将代码细节置于视图和模型之外。
创建项目结构
在本文中,一个简单的时间输入应用程序 — 称为 ChronoLog — 将演示如何使用 PHP 服务构建一个 Flex UI。这个示例需要在您的 Eclipse 工作空间中建立两个项目:一个是 PHP 项目,用于服务;另一个是普通项目,它包含一个作为 Ant 构建器建立的 Ant 文件。这个 Ant 构建器用于执行 Flex 编译器,以编译用于 UI 的项目。
要创建服务 PHP 项目,从 Eclipse 菜单中选择 File > New > Project。选择 PHP Project,然后选择 Create project from existing source 并在文件服务器的文档根目录下选择一个位置。这将允许您快速测试所创建的 PHP 服务。
要创建 UI 项目,使用 File > New > Project 并选择 Project 创建一个空项目。
创建这个 UI 项目后,创建一个名为 src 的文件夹,并在该文件夹中创建一个文件夹 chronolog。在 chronolog 文件夹下,分别创建 events、model、views 和 maps 文件夹。这个项目的结构如下图所示。
图 1. UI 项目结构
下载并安装 Mate
Mate 下载文件是一个已编译文件,扩展名为 .swc。从相关站点下载这个文件,安装方法是将其导入这个 UI 项目中的 libs 文件夹。结果如图 2 所示。
图 2. libs 文件夹中的 Mate .swc 文件
构建一个简单的服务
这个服务用 PHP 编写,只拥有两个类:一个是执行函数的服务类,一个是 TimeEntry UI 类映射到的 PHP 类。
编写这些服务类之前,下载并安装需要的 AMFPHP 文件。要运行这个示例,只需将 amfphp 文件夹安装到您的 PHP 项目中(如下所示)。
图 3. amfphp 文件夹
这个服务类显示在 清单 1 中。这个服务类只是在一个临时位置中以一个 XML 小文件的形式写出一些值。在您的计算机上,您也许需要修改这个位置(粗体),以便它能够被成功写出。您需要将它更改为一个能够被您的 Web 服务器读写的文件的名称。
清单 1. amfphp/services/ChronoLogManager.php 文件
<?php
require_once('./vo/TimeEntry.php');
/*
* A service for managing time entries.
*/
class ChronoLogManager
{
public function saveTimeEntry($entry)
{
$myFile = "/tmp/time.xml";
$fh = fopen($myFile, 'w') or die("can't open file");
$stringData = "<projects>";
$stringData .= "<entry project=\"$entry->project\" " .
"time=\"$entry->time\" user=\"$entry->username\" />";
$stringData .= "</projects>";
fwrite($fh, $stringData);
fclose($fh);
}
}
?>
|
服务方模型对象 TimeEntry.php 显示在 清单 2 中。AMFPHP 要求值对象存储在 services/vo 文件夹中,因此,这个 PHP 文件相对于 PHP 项目的根目录的相对路径名称是 amfphp/service/vo/TimeEntry.php。AMFPHP 要求类名和文件名(不包含扩展名)一致。
清单 2. amfphp/services/vo/TimeEntry.php 文件
<?php
class TimeEntry
{
var $_explicitType = 'model.TimeEntry';
public $username;
public $project;
public $time;
}
?>
|
使用类作为参数对象以代替多个参数的好处是,如果这个时间输入字段要添加更多字段,只需向这个对象添加一个新字段。但也有一个坏处,使用即时通讯框架时遇到的任何问题可能会伴随复杂的类型发生。有时,简单的类型(字符串、整数和布尔型)更容易使用,因为它们不容易出问题。
使用一个模型对象
模型对象是一个 ActionScript 类,它携带应用程序中的数据并映射到 PHP 服务层中的一个远程对象(TimeEntry.php)。您可以将这个对象作为一个方法 — 或一个方法的结果 — 的参数,而无须构建数组在 UI 和服务之间传递数据。
通常,用一种技术构建 UI,用另一种技术构建服务时,您必需编写或找到一个框架,以两种技术都理解的某种格式序列化这些类。经常使用 XML,因为通常很容易找到用现代编程语言编写的、读写 XML 的工具。
在这个应用程序中,AMFPHP 用于将 Flex 对象映射到远程 PHP 对象。Adobe Message Format (AMF) 是 Adobe 用于序列化和反序列化对象的格式。使用 AMF 的一个好处是它允许您连接这些对象,无需自己编写在 UI 和服务之间序列化和反序列化的代码。有几个框架用于通过 AMF 连接 Flex 和 PHP,其中一个来自 Zend(见 参考资料)。
清单 3 中显示了 TimeEntry 的模型对象(包含每个时间输入字段的信息)。将它存储到 UI 项目的 src/chronolog/model 文件夹中。
清单 3. src/chronolog/model/TimeEntry.as 文件
package model
{
[Bindable]
[RemoteClass(alias='TimeEntry')]
public class TimeEntry
{
private var _username : String;
private var _project : String;
private var _time : String;
public function TimeEntry()
{
}
public function get username() : String
{
return _username;
}
public function set username(value : String) : void
{
_username = value;
}
public function get project() : String
{
return _project;
}
public function set project(value : String) : void
{
_project = value;
}
public function get time() : String
{
return _time;
}
public function set time(value : String) : void
{
_time = value;
}
}
}
|
除了在这个类中声明的 RemoteObject 属性外,这个类就像您创建来支持应用程序的其他 ActionScript 类一样。
构建一个事件
Mate 是一个事件驱动的框架,因此使用 Mate 时要创建许多事件。一个 Mate 事件是一个 ActionScript 类,它从基 flash.events.Event 类扩展而来。一个字符串常量识别一个特定事件,一个事件类中可以有一个或多个常量。我使用这个功能来根据事件在业务领域中的目的对事件分组。例如,在这个 ChronoLog 示例中有一个 TimeEntryEvent 类。这个类使用常量 SAVE,这个常量表示一个事件将触发一个存储时间输入的函数。随着项目发展,这个类中可能包括更多事件,比如 GET 或 DELETE。
使用业务术语命名您的事件并将它们映射到业务函数。不要创建称为 ButtonEvent 的事件类,也不要添加称为 SAVE_CLICKED 的常量。否则,您将使用不好的命名方法,从而限制了事件的重用。如果用户改变主意且按钮改为一个链接,这种命名方法将导致那些过于紧密地绑定到 UI 的事件出现问题。
清单 4 中显示了 TimeEntryEvent ActionScript 类的一个示例。除了添加 SAVE 常量之外,构造器中的 bubbles 参数的默认值更改为 true。我为这个事件添加了一个类型为公共变量的参数 TimeEntry。
清单 4. src/chronolog/events/TimeEntryEvent.as 文件
package events
{
import flash.events.Event;
import model.TimeEntry;
public class TimeEntryEvent extends Event
{
public static const SAVE : String = 'TimeEntryEvent_SAVE';
public var entry : TimeEntry;
public function TimeEntryEvent(type:String, bubbles:Boolean=true,
cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
}
}
|
构建一个视图
除了 ActionScript 类,UI 项目中的其余文件都是 Flex 组件。这些组件放置在扩展名为 .mxml 的文件中。MXML 文件是格式良好的 XML 文件,所以我将它们在 Eclipse 中关联到 XML 编辑器,以利用 XML 编辑器的功能,比如格式化和标记闭合。要创建 MXML 文件,选择 File > New > File 并给文件一个适当的名称。
对于 UI,主程序文件(main.mxml)只包含对主 EventMap 和对视图的引用。这个 main.xml 的源在清单 5 中显示,main.mxml 文件存储在这个 UI 项目的 src/chronolog 文件夹中。
清单 5. src/chronolog/main.mxml 文件
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
xmlns:maps="maps.*"
xmlns:views="views.*">
<mx:ViewStack>
<views:DetailView id="detailView" />
</mx:ViewStack>
</mx:Application>
|
视图 detailView 是一个 Flex 组件,它扩展 Flex Panel 对象。这个视图的整个内容在下面显示。
清单 6. src/chronolog/views/DetailView.mxml 文件
<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" layout="horizontal"
width="100%"
height="100%"
xmlns:mate="http://mate.asfusion.com/">
<mx:Script>
<![CDATA[
import com.asfusion.mate.events.ResponseEvent;
import mx.controls.Alert;
import model.TimeEntry;
import events.TimeEntryEvent;
[Bindable]
public var entry : TimeEntry;
private function save() : void
{
entry = new TimeEntry();
entry.username = 'jdoe';
entry.project = projectInput.text;
entry.time = timeInput.text;
}
private function handleResult(event : ResponseEvent) : void
{
Alert.show('Success!');
}
private function handleFault(event : ResponseEvent) : void
{
Alert.show(event.fault.toString());
}
]]>
</mx:Script>
<mx:HBox>
<mx:FormItem label="Project Number">
<mx:TextInput id="projectInput" />
</mx:FormItem>
<mx:FormItem label="Time">
<mx:TextInput id="timeInput" />
</mx:FormItem>
<mx:Button label="Save" click="save()" />
</mx:HBox>
</mx:Panel>
|
这个视图包含两个字段和一个按钮。这些字段是项目编号和一个表示已工作时间的值。一个用户名(它也用于这个时间输入字段)是硬编码的。随着应用程序运行,用户名来自用户登录该程序时获取的数据。将这个视图作为一个 MXML 文件保存在 views 文件夹中,名为 DetailView.mxml。
构建 EventMap
一个扩展 Mate 的 EventMap 的组件用于将事件映射到动作,这是将事件链接到远程对象。另外,EventMap 可以用于使事件在结束时调用其他事件,设置本地对象上的值,以及在本地启动方法。
应特别指出的是,链接事件功能强大。通过链接事件,您可以避免对许多交互进行硬编码,使项目更适应不断变化的业务需求。例如,如果用户希望在成功保存数据后界面变为另一个视图,您可以创建一个 NavigateEvent 来实现这种更改。TimeEntry.SAVE 事件能够调用 NavigateEvent。这样,如果用户稍后改变主意,您只需更新 EventMap 来反映新的需求。
清单 7 中显示了 MainEventMap 的一个示例。它只是在 maps 文件夹中创建的一个 .mxml 文件。
清单 7. src/chronolog/maps/MainEventMap.mxml 文件
<?xml version="1.0" encoding="utf-8"?>
<mate:EventMap
xmlns:mate="http://mate.asfusion.com/"
xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import events.TimeEntryEvent;
]]>
</mx:Script>
<mate:EventHandlers type="{TimeEntryEvent.SAVE}">
<mate:RemoteObjectInvoker destination="amfphp"
source="ChronoLogManager"
method="saveTimeEntry"
arguments="{event.entry}">
<mate:resultHandlers>
<mate:ServiceResponseAnnouncer type="result" />
</mate:resultHandlers>
<mate:faultHandlers>
<mate:ServiceResponseAnnouncer type="fault" />
</mate:faultHandlers>
</mate:RemoteObjectInvoker>
</mate:EventHandlers>
</mate:EventMap>
|
在 EventMap 中,RemoteObjectInvoker 包含将事件类型 TimeEntryEvent.SAVE 映射到远程对象(ChronoLogManger)上的正确方法(saveTimeEntry)的信息。RemoteObjectInvoker 输入还指定参数 —
{event.entry} 记录的事件的输入字段。目标 amfphp 是在 services-config.xml 文件中指定的目标的 ID。
创建 EventMap 之后,将它包含到主程序中(如清单 8 所示)。
清单 8. main.mxml 文件中的 MainEventMap
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
xmlns:maps="maps.*"
xmlns:views="views.*">
<maps:MainEventMap id="eventMap" />
<mx:ViewStack>
<views:DetailView id="detailView" />
</mx:ViewStack>
</mx:Application>
|
分发视图中的事件
使用 Mate 可以以多种方式分发事件。您可以使用在每个组件中出现的分发器。也可以使用 Mate 的 Dispatcher,这个 ChronoLog 应用程序使用的就是 Dispatcher。使用 Dispatcher 的一个好处是您可以将事件上的参数轻松设置为本地值(比如文本输入),而无需编写任何 ActionScript 代码来完成这个任务。而且,使用 Dispatcher 可以设置在事件结束时执行方法。
这个 Dispatcher 在下面的代码中用粗体显示。
清单 9. 在 src/chronolog/views/DetailView.mxml 文件中显示的 Dispatcher
<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" layout="horizontal"
width="100%"
height="100%"
xmlns:mate="http://mate.asfusion.com/">
<mx:Script>
<![CDATA[
import com.asfusion.mate.events.ResponseEvent;
import mx.controls.Alert;
import model.TimeEntry;
import events.TimeEntryEvent;
[Bindable]
public var entry : TimeEntry;
private function save() : void
{
entry = new TimeEntry();
entry.username = 'jdoe';
entry.project = projectInput.text;
entry.time = timeInput.text;
saveDispatcher.generateEvent();
}
private function handleResult(event : ResponseEvent) : void
{
Alert.show('Success!');
}
private function handleFault(event : ResponseEvent) : void
{
Alert.show(event.fault.toString());
}
]]>
</mx:Script>
<mx:HBox>
<mx:FormItem label="Project Number">
<mx:TextInput id="projectInput" />
</mx:FormItem>
<mx:FormItem label="Time">
<mx:TextInput id="timeInput" />
</mx:FormItem>
<mx:Button label="Save" click="save()" />
</mx:HBox>
<mate:Dispatcher id="saveDispatcher" generator="{TimeEntryEvent}"
type="{TimeEntryEvent.SAVE}">
<mate:eventProperties>
<mate:EventProperties entry="{entry}" />
</mate:eventProperties>
<mate:ServiceResponseHandler result="handleResult(event)"
fault="handleFault(event)" />
</mate:Dispatcher>
</mx:Panel>
|

 |

|
调用服务
要调用服务,您需要在 services-config.xml 文件中的 AMFPHP 服务配置文件中建立 PHP 类。services-config.xml 文件包含服务的端点 URL,这个 URL 适用于支持 AMF 的框架。services-config.xml 文件的示例如下所示。
清单 10. src/services-config.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<services-config>
<services>
<service id="amfphp-flashremoting-service"
class="flex.messaging.services.RemotingService"
messageTypes="flex.messaging.messages.RemotingMessage">
<destination id="amfphp">
<channels>
<channel ref="my-amfphp" />
</channels>
<properties>
<source>*</source>
</properties>
</destination>
</service>
</services>
<channels>
<channel-definition id="my-amfphp"
class="mx.messaging.channels.AMFChannel">
<endpoint uri="http://localhost/chronolog/amfphp/gateway.php"
class="flex.messaging.endpoints.AMFEndpoint" />
</channel-definition>
</channels>
</services-config>
|
注意,您需要将 AMFPHP 使用的网关 URL 更改为您的环境使用的值。
构建项目
所有 Flex 文件就绪以后,Ant 构建器使用 Flex SDK 自带的 mxml.jar 文件编译项目。这个 Ant 文件如下所示。
清单 11. build.xml 文件
<?xml version="1.0"?>
<project name="chronologUI" basedir="." default="build">
<property name="flex.home" value="/home/ngood/bin/flex3-sdk" />
<property name="mxmlc.jar" value="${flex.home}/lib/mxmlc.jar" />
<property name="project.home" value="${basedir}" />
<property name="project.src" value="${project.home}/src" />
<property name="build.out" value="${project.home}/bin" />
<target name="build">
<java jar="${mxmlc.jar}" fork="true" failonerror="true">
<jvmarg value="-Xms256m"/>
<jvmarg value="-Xmx256m"/>
<arg value="-compiler.source-path=${project.src}" />
<arg value="+flexlib=${flex.home}/frameworks" />
<arg value="-compiler.library-path+=${project.home}/libs/Mate_08_8_1.swc" />
<arg value="-file-specs=${project.src}/chronolog/main.mxml" />
<arg value="-locale=en_US" />
<arg value="-services=${project.src}/services-config.xml" />
<arg value="-compiler.strict=true" />
<arg value="-warnings=false" />
<arg value="-output=${build.out}/chronolog.swf" />
</java>
</target>
</project>
|
build.xml 文件添加到项目之后,按照以下操作方法添加一个 Ant Builder:从 Project Explorer 选择 chronologUI 项目,然后选择 Project > Properties;从 Properties 窗口单击 New;从列表中选择 Ant Builder;在下一个界面中(见 图 4),单击 Browse Workspace 以查找并选择 build.xml 文件。这个构建器安装程序自动使用默认目标,因此您可以单击 Finish 关闭浏览器。要获取关于创建自己的项目构建器的更详细的说明,请参见 参考资料。
图 4. 创建 Ant 构建器
现在构建器已经添加到项目中,只需从 Eclipse 中的菜单中选择 Project > Build 就可以运行它。
由于这个示例使用 AMFPHP,必须有一个 AMF 目录以支持 AMFPHP 框架。关于如何建立和安装 AMFPHP 的更多信息请参见 参考资料。
运行示例
构建这个 UI 项目后,将 chronolog.swf 文件放置到您的 Web 浏览器的文档根目录之下,并在浏览器中导航到该文件(如下图所示)。
图 5. 运行完成后的应用程序
当您输入数据并单击 Save 之后,服务层将把数据写到在服务中指定的位置中的一个 XML 小文件。
结束语
使用 Flex SDK、Mate 和 PHP 可以构建富 Web 应用程序。只需对 Eclipse 项目设置进行一些微调,您就可以使用 Flex SDK 包含的 Flex Builder 在 Eclipse 中构建 Flex 应用程序了,尽管 Flex Builder 的 WYSIWYG UI 编辑和调试功能使构建 Flex 应用程序更加容易。
使用 Eclipse PDT 和 AMFPHP,您可以构建供 Flex 应用程序使用的服务。远程项目(使用 AMF)允许您快速编写从 Flex 调用远程对象上的方法的事件。
参考资料 学习
获得产品和技术
讨论
关于作者  | 
|  | Nathan A. Good 居住在明尼苏达州的 Twin Cities 市。他的职业是软件开发、软件架构和系统管理。在不编写软件时,他喜欢构建 PC 和服务器,阅读和尝试新技术,鼓励他的朋友参与开源软件开发。他独立编写并参与编写了许多书籍和文章,包括 Professional
Red Hat Enterprise Linux 3, Regular Expression Recipes: A Problem-Solution Approach 和 Foundations of PEAR: Rapid PHP Development。 |
对本文的评价
|