PRADO 2.0.1 快速入门教程 -- 第二章.使用PRADO编程

 

第二章.使用PRADO编程

现在让我们更深入的了解一下PRADO框架。这一章我们会介绍一些框架的基本概念和如何使用现有的PRADO组件来开发一个PRADO应用。


基本概念

组件

根据Clemens Szyperski的说法,“软件组件是一个具有特定接口的组合单元,可以独立存在,参与组成其它组建。

在PRADO中, 组件是TComponent 或者其子类的实例。PRADO框架中已经包括了TComponent类,它主要实现了组件的属性和事件机制。

  • 属性可以被认为是描述组件某一特性的公共变量,比如背景颜色,字体大小等等。属性是有类型的,比如 string, integer,等等。属性可以是只读的,也可以是可读写的。 属性和类成员的不同在于读写属性是通过getter和setter方法分别执行的。
  • 事件使代理函数可以对组件中进行的一些活动作出响应,比如鼠标的点击,输入文本的变化等。

一个组件会继承它所有父类组件的属性和方法。

一个完整的组件类定义包括两个文件:

  • 一个类文件,它定义了组件类的逻辑。这个文件主要是PHP代码。这个文件的文件名必须和类的名字一样,而且以.php 作为文件后缀。
  • 一个规格文件,它定义了组件类的属性和方法。这个文件是XML格式。如果这个组件类没有定义父类之外的新的属性和事件,那么这个文件是可以省略不要的。缺省情况下,这个文件和组件的类文件在一个目录中,而且名字是一样的,唯一不同的是它们的文件名后缀(类文件的后缀是 .php ,规格文件是 .spec )。值得注意的是,PRADO也允许你用不同的文件名来命名规格文件和模板文件,但是类文件名还是必须遵循与类同名,并且后缀为.php 的规则的。这时候你需要在类的常量 SPECIFICATION_FILE中指定规格文件的包含路径的文件名,可以使用绝对或者相对路径(相对类文件所在位置)。
控件

控件是TControl或其子类的实例,是定义了用户界面的PRADO 组件。

控件通过一个模板文件来指定他的内容。该模板可以包含静态文本,组件,或参与该模版最终显示的控件。模板文件的格式和HTML是很像的,只不过在HTML的基础上增加了一些特殊的标签来定义控件。缺省情况下模板文件和类文件在同一个目录下,而且文件名是相同的,模板文件的后缀则为 .tpl 。

页面

页面是TPage或其子类的实例化,是与用户直接交互的最高级别控件。页面负责协调控制页面上的控件的生命周期。(查看页面生命周期的小节详细了解)

模块

模块是TModule或其子类的实例化,它是为实现同一功能的页面组成的最高级别组件,是该组页面中重要的业务逻辑和数据的容器。

应用

应用是TApplication或其子类的实例,每一个PRADO 的Web应用都是一个简单的应用实例,包含一到几个页面或包含由页面组成的模块。应用实例负责协调各种处理器(见下文介绍)完成用户对页面的请求。

每一个PRADO应用都应该有一个XML格式的配置文件,用于描述应用级配置,如定义模块,指定处理器等等。

在创建应用后,通过调用pradoGetApplication()函数获得该应用(通常在入口文件脚本中)。

处理器

处理器是一种负责应用级任务的对象。PRADO当前定义了七种类型的处理器,每一个都可以让开发人员延伸并提供定制功能性。你可以访问Tapplication相关的getter方法调用这些对象。

session
Session处理器通过面向对象的方式实现了Session的功能。默认情况下,PRADO选择Tsession做为处理器对Php提供的 Session(session数据是存储在文件中的)功能进行简单的封装。Tsession中的Session功能默认是禁止的。
user
用户处理器以一个当前访问应用程序的用户对象的形式存在,在来自终端用户的多个请求之间持续存在。使用用户处理器必须先指定一个Session处理器。
cache
缓存处理器负责缓存已经创建的对象,用于提高创建相同的组件运行效率。默认情况下,PRADO使用TcacheManager处理器把实例化的组件缓存到内存和文件中。
request
Request处理器封装了用户请求信息,并负责对URL的解析。默认情况下,PRADO使用Trequest处理器,在PRADO应用中,该对象允许两种类型的地址格式,基于Get方式的,如:/index.php?page=HomePage&param=value, ,和基于地址方式的,如/index.php/page/HomePage/param/value。
parser
解析处理器负责解析组件配置文件和模版文件。默认情况下,PRADO使用 TResourceParser处理器。
locator
定位处理器负责定位组件配置文件和模板文件。默认情况下,PRADO使用TResourceLocator处理器查找文件目录下包含的组件类文件。
error
错误处理器负责根据不同页面的错误代码分发错误信息。默认情况下,PRADO使用TerrorHandler处理器。
vsmanager
显示状态处理器负责管理保持页面状态数据的存储。默认情况下,PRADO使用TviewStateManager显示状态处理器,该处理器使用Session进行存储。

命名规则和约定

PRADO是区分大小写,组件类型,属性名称,事件名,文件名等,都是大小写敏感的。

组件的类型,ID,属性和事件名称必须是正确的,以字母为开头,由字母数值和下划线组成的字符串。例如,DataSource和data2_source都是正确的命名,而 _dataSource和data$Source则是错误的。

一般约定,组件类型,ID,属性和事件名称的首字母是大写的(如:ButtonName,OnClick).推荐使用On开头命名事件,以区分事件和属性名称。

组件关系

组件相互之间存在特定关系的,您可以通过这种关系访问当前组件以外的其他组件。

父子关系

父子关系的确立用于帮助识别PRADO Web应用中的组件,这种关系同样可以被称为命名的包含关系。

每一个子组件都有一个ID,它可以在同一级别的控件中来唯一标识组件本身。在父组件内部,可以通过子组件的ID访问该组件。例如,ID为 Menu的组件是ID为HomePage组件的子组件,在HomePage内部,可以通过表达式$this->Menu访问该组件。

通过父子关系确立的组件树型层次中,每一个组件能够使用ID路径进行唯一标志。ID路径是层次结构中,由根组件到目标组件ID组成的序列。例如:在HomePage组件中,有一个Menu组件,Menu组件是MenuItem的父组件。在HomePage的树型层次中,我们可以通过ID 路径(Menu,MenuItem)定位到MenuItem组件。在Php代码中,可以表达为$this->Menu->MenuItem, 这里的$this代表HomePage组件。

通常情况下,一个页面中每一个控件都能被唯一标志是相当重要的,因此,控件必须是页面的“儿子”或“后代”,也就是说,控件必须处于页面组件的树型结构层次中。

组件的父子关系可以通过几种方式建立。因为组件定义在组件配置文件或模板文件中,Prado 框架将默认设置他们的父组件为该配置文件或模板文件的拥有者。同样,你可以使用TComponent::createComponent()的方式方便的创建一个新的组件,并把他添加为调用组件的子组件。

显示包含关系

控件之间以一种附加的显示包含关系互相关联。一个控件是另一个控件的容器,如果后者出现在容器控件中。

页面是最高级控件,因此不包含在一个容器中(当然你也可以认为application是它的容器)。页面的表现形式将直接显示到用户端。

Prado框架根据控件模板文件建立这种显示包含关系。一个控件的标签内附在另一个控件标签中,表明前者被包含在后者中。

代码中,可以通过调用TControl::addBody()建立显示包含关系。

实例化组件

组件有两种方式来实例化。一种方法是在一个控件的模板文件中定义它,当这个控件被创建的时候,框架会自动的去实例化这个组件。还有一种方法是在PHP代码中手动的实例化。我们称前者为组件的静态创建,后者为组件的动态创建。

静态创建组件

组件在一个组件配置文件或者组件模板文件的声名中被静态的创建。

组件的在组件配置文件中的声名语法如下,

<?xml version="1.0" encoding="UTF-8"?>
<component>
   ......
   <component type="component type" ID="component ID">
       <property name="property name" value="property value"/>
       ....
       <event name="event name" handler="function name"/>
       ....
   </component>
   ......
</component>

其中,type属性指定组件的类名,ID属性指定组件的ID. property和event标签分别指定了默认的属性值和事件响应函数。

控件在控件模板中通过以下标签声名:

......
<com:ComponentType ID="..." PropertyName="..." EventName="...">
   ....body content....
</com:ComponentType>
......

其中,ComponentType,PropertyName和EventName应该分别替换为真正的组件类名,属性名称和事件名称。ID属性是可选的,如果定义了ID属性,该值在同级的组件ID中必须是唯一的;如省略设置,PRADO框架将自动为该组件分配一个唯一的ID。当然,这要求组件的标签被正确的嵌套,每一个开放的组件 标签都应该和一个闭合的组件标签组成一对,这个规则和XML的规则是一样的。

以上两种范例中,被实例化的组件即为拥有该配置文件或模板文件组件的子组件。

在模板范例中,被实例化的组件将成为直接定义在该组件区域内的组件的容器。(包含该区域内的组件)。

属性的初始值字符串会被自动转换为正确的属性类型。目前有六种属性类型: string, integer, float, boolean, array 和 object. 前面三种类型的字符串格式是非常简单的。 boolean 类型只允许使用两个字符串: true 和 false。 array 类型接受如 (value1,value2,key3=>value3,...) 格式一样的字符串,这和 PHP 数组的初始化是一样的。 而 object 类型就比较复杂了,它取决于属性是如何被定义的。一些属性可以允许使用字符串,并会把它转换为对象,但是有些属性就不行了。

当在配置文件或者模板文件中定义了事件响应函数,该响应函数必须是父组件的一个方法函数。例如,你在一个模板页中定义了OnClick="onBtnClicked",那么onBtnClicked必须是该页面类的方法函数。

在上述规则中,有一种是例外的。如果响应处理函数名称包含点号的,则Prado框架将使用该名称定位响应的处理对象,而不是父组件。例如,OnClick="Container.onBtnClicked"将使用Container组件的onBtnClicked方法

当组件被创建(实例化)的时候,通过规格文件和模板文件定义的组件的属性和对应事件处理方法就会立刻生效了。

动态创建组件

PRADO允许开发人员使用php代码动态创建组件。新的组件可以通过三种方式进行创建。

 
1. $com=new componentType;
2. $com=pradoGetApplication()->createComponent($type,$id=null);
3. $com=$parent->createComponent($type,$id=null);

第一种方式使用简单的new操作符,该方式创建了一个新的组件,但没有设置它的ID值。

第二种方式根据$type参数创建一个新的组件,并且,如果提供的$id值不是null,该方式将同时设置新组件的ID。在创建过程中,这种方式可以使用缓存技术快速重复创建相同类型的组件。

第三种方式和第二种十分类似,新组件在创建之后将添加为调用的$parent组件的子组件。

因此,使用哪一种方式来实例化一个新的组件?这取决于以下因素,如果该新组件不使用任何资源处理器(如数据库连接,文件处理等),使用第二或第三种方式可以提高性能;如果该新组件不需要一个全局唯一的标志符(如不显示的组件),那么应该采用前两种方式。

创建页面和模块

页面和模块是特殊的组件,由应用实例直接管理。如果用户发送一个页面请求类型,应用实例将创建该页面。模块存在细微不同,模块必须定义在应用配置文件中,应用实例才知道哪些模块是可访问的(通过包含该页面定义的命名空间,页面才是可访问的)。每一个模块有一个ID,和组件ID一样,如果用户发送模块 ID的请求,应用实例将创建这个模块。

使用组件

访问组件属性和子组件

PHP5使用了一种很好的方式访问组件的属性及其子组件。

你可以把一个组件的属性当作组件的成员变量来使用。比如要设置TButton 控件的Text 属性,你可以使用$button->Text="xxx"的代码,这里$button 代表了控件的实例。你可以把一个组件的属性当作组件的成员变量来使用。比如要设置TButton 控件的Text 属性,你可以使用$button->Text="xxx"的代码,这里$button 代表了控件的实例。

如果子组件的ID和组件的属性名不冲突,你可以使用同样的简单方式来访问该子组件。例如,假定HomePage页有一名称MenuBar的子控件,则你可以通过$homepage->MenuBar的方式来访问该组件。

你可以链串一序列的属性名和组件ID来访问深层的属性或组件。例如,假定现在HomePage页面有一个子控件MenuBar ,MenuBar 控件有一个子控件HyperLink ,那么在页面环境中,可以使用的代码$this->MenuBar->HyperLink->NavigateUrl来读取 HyperLink 控件的NavigateUrl 属性。

注意,由于PHP5.0中的一个BUG,如果你需要设置一个属性的值,那么你必须首先通过它的控件的ID路径来获得控件,然后再来设置这个属性的值。在上面这个例子中,需要用下面两行代码来设置NavigateUrl 属性:

$link=$page->MenuBar->HyperLink;
$link->NavigateUrl="...";

如果直接使用 $this->MenuBar->HyperLink->NavigateUrl 来设置属性值会产生一个错误。你还可以选择调用setter函数来设置属性的值,这样可以避免错误产生。

$page->MenuBar->HyperLink->setNavigateUrl("...");
定义事件响应函数

事件响应函数通常在规格文件或者模板文件中指定给对应的事件,指定事件响应函数和指定属性的初始值是类似的。注意,在规格文件或者模板文件中指定的事件响应函数,必须在此规格文件或模板文件对应的组件中定义,或可以通过ID路径定位。

它的语法如下:

function handlerName($sender,$param)
{
   ...
}

这里 $sender 指向的是触发这个事件的控件, $param 是事件的参数,它的内容取决于事件的类型。

在编程的时候也可以使用TComponent::addEventHandler() 方法来动态的指定一个事件响应方法。

你可以为一个事件指定多个响应方法。当这个事件被触发的时候,所有指定的响应方法都被自动调用。所以,PRADO实现的是多点派发事件触发机制。

数据绑定

你可以给控件的属性绑定一个表达式,当这个控件的dataBind() 方法被调用的时候,这个属性的值回自动被设置为这个表达式的值。

数据绑定在开发数据组件时是非常有用的,这些组件的很多属性值都是来源于数据源提供的数据的。你可以在组件的规格或者模版文件中设定数据绑定,也可以在代码中设定。

在模版中设定数据绑定的话,只要给属性的值指定一个有效的PHP表达式的字符串,并在前面加上一个# 作为前缀。比如在页面模版文件中使用如下的代码:

<com:TLabel Text="#$this->Page->ID" /> 

这段代码给TLabel 组件的Text 属性绑定了一个的表达式$this->Page->ID 。这个表达式的作用是获得当前控件所在页面的ID。

注意,这个表达式中的$this指的是TLabel控件本身,因为$this所在上下文环境是在TLabel 中。

在代码中要设定数据绑定,可以调用组件的bindProperty() 方法,这时候不需要在前面加上字符# 。

注意,给属性绑定的表达式只有在dataBind() 被调用时才会计算该表达式的值,并把它赋值给属性。具体内容可以参考dataBind() 的相关文档。

另外,如果在模板文件中你需要给一个属性赋初始值,而不是数据绑定的话,如果这个值是以#开头的,那么应该将#重复一次,就像PropertyName="##...." 一样。

组件类树

目前发布的 PRADO 包括如下所示的组件树,这些组件的属性,事件和类的方法在PRADO API 文档中都可以查到。

TComponent
   TAdodb
   TModule
   TControl
       TExpression
       TForm
       TLiteral
       TPage
       TPlaceHolder
       TRepeater
       TRepeaterItem
       TStatements
       TWebControl
           TButton
           TCheckBox
               TRadioButton
           TFileUpload
           TFormLabel
           THyperLink
           TImage
               TImageButton
           TLabel
           TLinkButton
           TListBox
           TListControl
               TCheckBoxList
                   TRadioButtonList
               TDropDownList
               TListBox
           TPanel
           TTextBox
               TDatePicker
               THtmlArea
           TValidationSummary
           TValidator
               TCompareValidator
               TCustomValidator
               TRangeValidator
               TRegularExpressionValidator
                   TEmailAddressValidator
               TRequiredFieldValidator

页面生命周期

由于PRADO主要是一个重复执行页面的控制器,理解页面的生命周期对掌握PRADO编程是非常重要的。

首先我们要介绍一下postback的概念。我们把一个form的提交称之为postback,如果form的数据是提交给包含该form 的页面的。postback可以被认为是由用户在客户端触发的一个事件。PRADO会区分出把postback事件交给哪一个服务器端的组件来响应。如果找到了这个组件,比如是一个TButton ,那么我们就把这个TButton 组件称为事件的发送者(sender)。

页面在被请求调用的时候会经过几个状态。当一个页面是由于它发生了一个postback而被调用的时候,这个页面会经历以下的生命周期:

  • 页面的创建:页面和它所有子组件是被递归创建的。框架根据各个组件的规格文件和模板文件的配置来初始化组件的属性。你一个可以重载页面的构造函数来做一些其他的初始化工作,比如给一些属性赋一些缺省值。但是需要注意的是,在这一步的时候,是不能够访问其他组件,因为组件之间的关系这时候还没有被建立。
  • OnInit 事件: OnInit 事件会被广播给整个页面的组件树(也就是说所有的组件都会被触发OnInit事件)。可以重载页面的onInit() 方法或者给OnInit事件指定一个处理方法来做一些初始化的工作,比如建立数据库连接等等。这时候组件和组件的关系已经被建立了。
  • 导入显示状态(Viewstate):将先前的显示状态导入到页面中。页面自己会将上次显示给用户看的状态保存起来。关于显示状态,下一章会有更详细的解释。
  • 导入提交的数据:页面中的一些组件的属性会根据用户提交的数据而更新。比如,TTextBox 组件的Text 属性会被用户在此组件的文本输入框中输入的文本而更新。
  • OnLoad 事件:在这个阶段,页面会设置为提交前用户看到的状态。OnLoad 事件也会被广播给整个页面的组件树。同样也可以重载onLoad() 方法或者给OnLoad 事件指定一个处理函数。
  • 导入提交的数据:一些在OnLoad 步骤创建的组件在这时候有机会导入提交的数据。
  • 产生提交数据变化事件。如果一个组件的数据被用户改变了,那么就会机会产生一个事件来指明这个变化。比如用户改变了一个TTextBox 组件的文本输入框的值,那么这个TTextBox 组件就会触发一个OnTextChanged 事件。开发者能够为这个事件指定处理方法。
  • 输入验证:如果事件的提交者需要验证,这时候页面注册的验证组件会来对应的数据。
  • Postback事件:postback事件通过事件的发送者被触发。比如,用户点击了一个按钮引起了一次postback,那么这个按钮对应的TButton 组件就触发一个事件OnClick。你可以指定这个事件的处理函数来响应这个事件。
  • OnPreRender 事件:在这一步,页面已经完成了被提交的数据的导入和postback事件的处理。OnPreRender 事件会被广播给整个页面的组件树 。在页面被绘制之前,你可以重载onPreRender() 函数或者给OnPreRender 事件指定一个事件处理函数。
  • 保存显示状态:整个页面的组件树的显示状态被保存在一个固定的存储空间(比如表单的隐藏字段,session,或者数据库中)。
  • 页面显示:通过递归的方式页面类来显示这个页面,页面中的组件会自己显示自己(意思是说每一个组件都会负责显示它自己)。通常情况下,组件会在对应所在模板的位置上来显示自己。你也可以重载TComponent 的render() 方法来自己控制组件的显示。
  • OnUnload 事件:页面和它所有的子组件从内存中被释放。同样,这个事件也会被广播给整个页面的组件树。你可以通过重载onUnload() 函数或者给OnUnload 事件指定一个处理函数来完成一些清除工作,比如断开数据库的连接等等。

当页面是第一次被请求的时候,上述的生命周期会简单一些。具体来说,导入显示状态,导入提交的数据,产生提交数据变化事件,输入验证和postback事件这几个状态是没有的。

另外,如果一个模块和一个页面同时被请求,PRADO框架将在模块被实例化以后调用TModule::onLoad()(即页面在被实例化之前)。同样的,PRADO框架在应用程序结束之前将调用TModule::onUnload()。

以下图片显示了页面是由于发生了一个postback而被调用的时候的生命周期(感谢Todd Patrick提供这张漂亮的照片)

Image:Lifecycles.gif

应用程序的配置

每一个PRADO应用都应该有一个XML格式的配置文件。在"hello world"这个例子中,这个文件的文件名为 application.spec。

如下所示,我们以PRADO blog中的应用配置文件的格式为范例,

<?xml version="1.0" encoding="UTF-8"?>
<!-- 
 application specification
 @ID (required): the ID of the application
 @state (default=on): the state of the application.
   If @state=on, the application is in production working.
   If @state=off, the application is in maintenance/stop state.
   If @state=debug, the application is in development state.
-->
<application ID="blog" state="debug">
 <!-- 
   request class (optional): the class encapsulating request info.
   @class (default=TRequest): the request class name.
   @default (when @class=TRequest, required): 
       the default page name (may include module name).
   @format (when @class=TRequest, default=get): the format for parameter 
       representation in URL.
     If @format=get, URL looks like: /index.php?name1=value1&name2=value2
     If @format=path, URL looks like: /index.php/name1/value1/name2/value2
 -->
 <request default="Blog:ViewAllPage" format="get" />
 <!-- 
   user class (optional): the class representing the user object.
     @class (required): the user class name (must implement IUser).
 -->
 <user class="BlogUser" />
 <!-- 
   session class (optional): the class responsible for session management.
   @class (default=TSession): the session class name (must implement ISession).
   @cache-expire (when @class=TSession, default=0): cache expire time (minutes).
   @save-path (when @class=TSession, default=' '): directory storing session files.
 -->
 <session enabled="true" />
 <!-- 
   cache manager class (optional): class responsible for caching components
   @class (default=TCacheManager): the cache manager class name.
   @enabled (when @class=TCacheManager, default=false): whether to use caching
   @path (when @class=TCacheManager, optional): the directory for saving data.
 -->
 <cache enabled="true" />
 <!-- 
   resource parser class (optional): the class responsible for parsing 
       component specs and templates.
   @class (default=TResourceParser): the resource parser class name.
   <parser class="TResourceParser" />
 -->
 <!-- 
   resource locator class (optional): the class responsible for locating 
       component specs and templates.
   @class (default=TResourceLocator): the resource locator class name.
   @js-path (default=js): URI (relative to entry script) storing js files.
   @css-path (default=css): URI storing css files.
   @image-path (default=images): URI storing image files.
   <locator class="TResourceLocator" />
 -->
 <!-- 
   error handling class (optional): the class responsible for handling errors.
   @class (default=TErrorHandler): the error handling class name.
 -->
 <error>
   <!--
     If @class=TErrorHandler, the following elements are acceptable
       <when error="ErrorName" page="PageName" />
             specify pages responsible for different errors.
       <otherwise page="PageName" />
             specify the page if the error is not recognized.
   -->
   <when error="SiteOff" page="ErrorPage" />
   <when error="PageNotFound" page="ErrorPage" />
   <when error="Unauthorized" page="ErrorPage" />
   <when error="Forbidden" page="ErrorPage" />
   <when error="InternalError" page="ErrorPage" />
   <otherwise page="ErrorPage" />
 </error>
 <!-- 
    viewstate manager class (optional): the class responsible for
        locating component specs and templates.
    @class (default=TViewStateManager): the resource locator class name.
    @enabled (default=false): whether to use viewstate manager.
    @buffer-size (default=10): how many page viewstates should be kept.
    @key (default=''): the key used to generate HMAC and encryption.
    @encrypt (default=false): whether to encrypt the viewstate data.
    <vsmanager class="TViewStateManager" enabled="true" key="secret key" encrypt="false" />
 -->
 
 <alias name="BlogApp" path="." />
 
 <using namespace="System.Web.UI.WebControls" />
   <using namespace="System.Security" />
   <using namespace="BlogApp.global" />
 
 <parameter name="DSN">sqlite://blog%2Fblog.db</parameter>
 
 <module ID="User" class="DataModule">
   <using namespace="BlogApp.UserModule" />
   <secured page="EditPage" />
   <parameter name="AllowNewAccount">true</parameter>
 </module>
 
 <module ID="Blog" class="DataModule">
   <using namespace="BlogApp.BlogModule" />
   <secured page="EditPage" />
   <secured page="NewPage" />
   <parameter name="AllowAllDelete">true</parameter>
 </module>
 
</application>

上面的应用配置文件中,大多数的标签元素在已经在注释中给出说明。

应用程序包含状态属性,on,off或debug。该状态将决定错误信息是如何显示的。应用程序的ID属性主要用于给指定的Session变量名增加前缀(以防止多个应用程序互相干扰)。

alias 元素定义了文件路径的别名,路径可以是绝对路径或相对路径。

using 元素定义了要增加到 PHP搜索路径 include_path 变量中的命名空间。命名空间是用"."号连接的。第一段是路径的别名,之后是子目录。比如System.Web.UI 代表了框架所在目录下的Web/UI/子目录。框架的目录的别名已经被定义成System 。在编程的时候,也可以调用using() 函数来增加一个命名空间。

secured 标签知名了页面是否需要验证/授权,page 属性指名的是页面的名称或者是页面名称的匹配表达式。如果一个页面被标明是的,那么说明这个页面需要用户验证的。另外如果role 属性不是空的,那么要求用户是属于指定的角色的。

parameter 元素定义了用户参数。可以通过设置parameter 元素的file 属性来导入一个参数文件。该参数文件可以是相对路径或决定路径,参数文件的格式如下,其中注释与应用配置文件类同,

<?xml version="1.0" encoding="UTF-8"?>
<parameters>
   <parameter name="...">...</parameter>
   <parameter name="...">...</parameter>
   <parameter file="...">...</parameter>
</parameters>

Module元素定义了应用授权的模块。一个模块可以使用自己的命名空间,定义用户参数的模块访问级别,并指定模块内页面是否授权。每一个模块拥有一个区分其他有效模块的唯一ID。对于模块及其页面的有效的命名空间可以使用全局的(模块外部定义)和使用模块内部定义的。模块类和页面必须是可以通过命名空间定位的。

URL地址格式

PRADO中使用的URL请求格式取决于应用配置中Request的处理定义,默认情况下,使用TRequest识别以下Url格式,

/examples/phonebook.php?page=AddEntryPage

这段代码将会请求 AddEntryPage 页面。如果没有指定page 变量,那么就会调用应用配置中的缺省页面。

如果使用了模块,格式有一些不同,如下在页面类型中加入了模块的ID做为前缀,

/examples/blog.php?page=User:NewPage

如果Web服务器支持改写URL,TRequest同样支持URL格式为路径格式,如下:

/examples/blog.php/page/User:NewPage

详细的实现说明,可参照TRequest文档。

你也可以使用 TApplication::constructUrl() 方法来请求一个页面。

定义新组件

可以通过两种方法来定义新的组件类:继承和组合。

继承是面向对象的设计概念。派生类定义它的父类的一些内容,还可以提供一些其他的功能。在PRADO框架中,组件的属性和事件都是可以继承的。派生类也可以提供更多的属性和事件。所有的组件类都应该从TComponent 或者它的派生类继承。

所有父组件的属性和事件会被派生组件类继承。

组合是用于基于组件的框架的。在PRADO中,主要对控件使用。一个新的控件类可以被定义为几个其他几个控件的组合。新的类负责协调这些控件之间的通信,并代表它们和外界通信。比如可以定义一个LabeledTextBox 控件类(继承TControl ),它由TLabel 一个控件和一个TLabel 控件组成。新的类负责配置这两个组成控件的属性和方法。

一般的约定(并不强求),控件的ID和属性的首字母应该大写,比如HomePage, NavigateUrl, LogoPict;事件以On开头,比如OnClick 。ID和属性的名字命名和变量命名一样,必须是以字母开头的,仅包括字母数字和下划线的字符串。

要定义一个新的组件通常需要写一个组件类文件,一个规格文件和一个模板文件。有些时候后面两个文件可以不要的。

定义属性

组件的属性在组件类的规格文件中定义,规格文件的语法如下:

<?xml version="1.0" encoding="UTF-8"?>
<component>
   ......
   <property name="property name"
             get="getter method"
             set="setter method"
             type="property type"
             default="default value" />
   ......
</component>

在这里,name 用来唯一标识属性;get 定义了一个类方法来读取属性,set 定义了一个类方法来写属性,type 定义了属性的类型(string, integer, float, boolean, array 和object),default 标识了属性的缺省值。其中name 属性是必须的。getter方法的语法如下:

function getterMethodName()
{
   ....
   return $value;
}

如果一个属性是不可读的,那么不要设置get 属性。 setter方法的语法如下:

function setterMethodName($value)
{
   // set some variable with $value
}

如果一个属性是不可写的,那么不要设置set 属性。如果这个属性的类型是string,那么也可以不指明。类型的属性用来自动转换配置在规格文件或者模板文件中的属性初始值。default 属性一般用来作为引用的目的,也是可选的。如果一个属性是可读的而且之前没有被设置,那么getter方法应该返回一个缺省值。

[ edit]
定义事件

组件事件在规格文件中定义,而其内在的实现机制在类文件中定义。在规格文件中定义事件的语法如下:

<?xml version="1.0" encoding="UTF-8"?>
<component>
   ......
   <event name="..." />
   ......
</component>

事件的name应该是一个合法的变量名。

在类文件中,通常会实现这么一个对应的方法(假定事件的名称叫 OnClick):

function onClick($param)
{
   $this->raiseEvent('OnClick',$this,$param);
}

当这个事件实际发生的时候,这个 onClick 方法应该确实被调用了(参考框架的TButton.php 文件)。raiseEvent 方法在TComponent 类中实现,它调用了所有对应OnClick 的事件响应函数,并把参数$param 传递给它们。

[ edit]
编写模板文件

在这一节里,会讲一些关于如何编写模板文件。模板是用于控件的,但也不是所有的控件都需要模板。比如TTextBox, TButton 这些控件因为没有内容,因此就不需要模板。模板文件一般用于页面或者基于组合定义的控件。 模板文件格式在资源解析处理器中详细定义,该处理器在应用配置文件中指定。默认解析处理器是TResourceParser,我们将主要介绍一下它认可的格式。模板中的注释,比如 被作为静态文本处理。 可以在组件标签之外通过属性标签<prop:...>来配置组件的属性。比如可以用以下的代码配置模板中TTextBox 控件的Text 属性。

...
<com:TTextBox ID="profile">
  <prop:Text>
    ...
  </prop:Text>
</com:TTextBox>
...

在一对属性标签中间的内容作为对应的组件属性的值。如果一些属性的值需要很大的数据,这样就很方便。

在模板文件中可以使用3种特殊的标签:

  • <%include ... %> - 框架会在解析模板前在这个位置包含一个外部文件。比如,你可以用它在页面模板中包含一个公共头文件<%include header.tpl %> 。外部文件的路径应该是这个模板文件的相对路径。

在最新的版本中,上文提到的“paths”是一个命名空间。因此,如果你的application.spec文件中存在以下语句 <alias name="Pages" path="." /> 并且你的文件调用"login.tpl"文件,你可以使用: <%include Pages.login %> 注意:在调用路径中不包含.tpl,显然,它是自动加上去的。


  • <%= ... %> -会直接在这个位置上输出一个PHP表达式的值,比如<%= date('Y-M-d',time()) %>。注意,表达式后面不需要输入分号。这个表达式的上下文环境是这个模板对应的组件,可以使用$this 变量来在表达式中使用组件。表达式的值在组件的绘制状态时计算。
  • <% ... %> - 代表了一系列PHP的语句。如果它们当中有echo或者print,那么其结果会显示在标签对应的位置。注意,这里PHP语句需要使用分号隔开,和标准的 PHP语法是一样的。同样,这里的上下文环境依然是拥有此模板文件的组件。(译者注:很多地方都提到了上下文环境,这个词在有些面向对象的书籍中是这么翻译的,而有时候也被翻译成其他文字。

在模板文件中,除了在组件标签范围内,在其他以外的地方可以自由使用其他各种标签。

总结

这一节我们来总结一下如何基于PRADO来开发一个Web应用。

首先需要创建两个文件:应用的入口文件和应用的配置文件。这一部分请参考本手册前面的"Hellow,world!"这个例子。

对于一个完整的Web应用而言,你还需要创建一个或者多个页面。每一个页面都需要有一个页面类文件,另外可能还需要一个模板文件或者规格文件。在模板文件中,你可以把组件和其他静态文本根据最终需要显示给用户看的样子组织在一起。在规格文件中,可以定义页面的属性,事件和子控件,这些定义可以在模板文件和代码中使用。这两个中都可以设置组件的初始值和指定事件的响应函数。在页面类文件中编写事件响应函数和其他函数。

有些时候,为了重用代码,你可以定义新的组件。比如你可以定一个SideBar组件来显示用户能看到的菜单,这样在各个页面中就都可以直接使用这个SideBar 组件来显示菜单了。

PRADO引入了PHP5新的异常处理机制,能够显示堆栈中的错误信息。这样在调试的时候你可以精确的找到什么方法发生了什么错误。

发布 PRADO 应用则非常简单。框架建议使用相对路径,因此如果你是这么做的话,只需要把包含你的应用的代码拷贝到你想放置的目录中就可以了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值