喜悦国际村 
» 游客:  注册 | 登录 | 搜索 | 统计 | 帮助

RSS 订阅当前论坛  

喜悦证交所已经关闭

上一主题 下一主题
 26  1/3  1  2  3  > 
     
标题: Phpsa Mvc框架使用系列教程(包含示例代码)  
 
teacherli (teacherli)
中级会员
Rank: 3Rank: 3
中级会员



UID 29240
精华 4
积分 402
帖子 322
金钱 361 喜悦币
威望 40
人脉 1
阅读权限 30
注册 2004-6-8
来自 北京
状态 离线
Phpsa Mvc框架使用系列教程(包含示例代码)

1. 什么是phpSA framework框架?
        phpSA framework 是一个基于MVC思想的PHP开发框架,结合Servelet中的doGet(),doPost()方法及struts中的思想,形成一个适合于PHP语法特征的框架类,主要用来解决PHP开发中显示与逻辑的关系问题。
        phpSA framework组合了Smarty, AdoDBLite, 通过划分逻辑、显示与控制层来实现PHP的企业开发。
       
2. 主要功能:
2.1 多语种。系统采用UTF-8作为默认编码,同时可自行定义程序不同语种编码。
2.2 区分业务逻辑与显示。显示采用smarty标准语法加自定义组件,业务层通过封装ADODBLite实现自动连接。
2.3 项目模块开发。框架设计原则采用模块式开发,将整个项目划分不同模块,每个模块中实现不同功能。
2.4 角色管理自定义。系统基于角色进行执行权限管理,默认分为两种用户角色:GUEST用户与USER登录用户角色,通过XML配置文件进行定义两种角色的不同权限。实际使用中可根据项目不同情况自行定义角色类别。
2.5 快速原型。使用框架可在系统界面完成后快速搭建模拟运行平台,实现系统原型,通过对系统原型的扩展实现程序功能,且不会产生任何垃圾代码。

3. 目前版本:
3.1 框架: 版本:0.9 alpha,
3.2 Smarty版本(http://smarty.php.net):2.6.8   
3.3 ADODBLite版本(http://adodblite.sourceforge.net/index.php): 1.0.3

4. 基本原理:
phpSA framework采用统一的入口文件进行管理程序各类请求,通过不同的参数值来确定不同模块中的不同动作,入口文件一般情况为index.php, 访问路径类似于下面这样:
index.php?action=user!login
index.php?action=user!reg
index.php?action=news!add
index.php?action=news!del&id=1
访问路径以action作为文件入口参数,action的值包含以!分开的两部分,前一部分为模块名称,后一部分为模块中的动作名称,上述的访问路径中包含了两个功能模块:user与news, 其中user模块中包含两个动作,分别为login, reg; news模块中有两个动作,分别为:add、del。

phpSA framework中主要分为四个部分:Application, Form, Action, BusinessService。下面来分别介绍一下各个类的主要作用:
Application类: 入口文件实现此类的实例,调用这个类的process方法执行访问路径中指定的方法,主要完成:
1. 对请求路径进行认证,拒绝执行非授权路径
2. 获取请求所对应的Form类,实例化Form类。在请求没有所对应的Form类时返回NULL,继续执行下面操作.
3. 获取请求所对应的Action类, 实例化Action类,传入Form类
    4. 调用Action类的execute方法,根据返回的Forward对象(或Redirect对象)进行重定向。
  
  Form类:当有表单进行POST时初始化此方法
  1. 对POST表单数据进行封装,这个操作由PHP的魔术函数来实现,对$_POST["name"]这类的变量进行封装,采用类JAVA Bean的方式进行操作, $form->getName()取得$_POST["name"]值, $form->setName("xxx")设置变量值。
  2. 对POST表单字段进行验证。当设置了Form类的validate为true时,自动调用Form类的validate方法进行表单字段验证,验证方式采用struts ActionForm类似方式,违反约束时返回ActionMessage, 它的作用在Action对象中介绍。
  
  Action类:Action类根据访问请求的不同它可以分为执行动作,显示执行界面两种功能。
  1. doView(): 用来显示界面。组合实现Smarty的内部成员变量$view, 通过设置模板变量进行显示界面。例如新闻模块中的查看新闻动作index.php?action=news!showNews&id=1; 这个访问请求主要是用来显示一个新闻信息,因此在showNewsAction中我们实现doView方法供Application调用,完成显示动作.
  2. doAction(): 用执行一个涉及业务逻辑的动作。例如:新闻模块中的删除动作:index.php?action=news!del&id=1;这个动作不用显示界面,执行完成后直接跳转到另一个动作或是显示中去。它的返回值有两种,一种为跳转类返回,包含Forward与Redirect, 另一种为ActionMessage, 用来对传入的表单字段进行逻辑验证,例如像用户注册检测用户名是否已经存在,如果存在返回一个包含错误信息的ActionMessage,然后重新调用doView进行修正表单字段。
  3. 既包含显示又包含动作:指在一个Action中既有显示界面又执行这个动作。像新闻添加index.php?action=news!add,这个请求包含两部分内容,一部分为显示添加新闻的界面,另一部分为执行添加新闻的动作,这样,我们可以在Action中既定义doView()又定义doAction(), 在doView调用时显示添加新闻的主界面,在doAction()动作调用中执行添加新闻,添加新闻返重新返回执行doView(); 决定调用doView与doAction的因素是是否使用POST的方式传递表单数据。
  4. 特殊的请求。特殊的请求指以GET方式提交的表单且没有单独的显示界面,例如:新闻搜索index.php?action=news!search&key=test&page=1, 没有自已独立的显示界面,而由其它页面直接GET提交到这里的这类情况。处理这类请求时将action以隐藏字段的方式放入表单进行传递,考虑到它实际是先执行一个动作,然后把动作显示出来我们使用doView进行处理,在doView中调用业务逻辑后返回结果,然后再使用View对象显示出结果。

  BusinessService类:业务逻辑层,组合ADODBLite, 负责与数据库进行交互及实现涉及逻辑的一切操作。其基类baseBusinessService完成对$db类的初始化,继承类中定义不同的成员函数来实现各类业务逻辑操作,这此成员函数由action类进行调用。为了简化程序,一个程序模块中一般只采用了一个公用类xxBusinessService进行业务逻辑处理。实际使用中可以加入多个逻辑类,然后统一由xxBusinessService类的成员函数进行调用,然后再由Action进行调用。

下面是框架执行Action类的doView(), doAction() 的执行序列图:


 附件: 您所在的用户组无法下载或查看附件
2005-12-20 11:49 PM#1
查看资料  发短消息  QQ . .   顶部
 
teacherli (teacherli)
中级会员
Rank: 3Rank: 3
中级会员



UID 29240
精华 4
积分 402
帖子 322
金钱 361 喜悦币
威望 40
人脉 1
阅读权限 30
注册 2004-6-8
来自 北京
状态 离线
[推荐阅读] 庆祝村子复活2: OReilly.Essential.PHP.Security 下载
doAction

 附件: 您所在的用户组无法下载或查看附件
2005-12-20 11:54 PM#2
查看资料  发短消息  QQ . .   顶部
 
teacherli (teacherli)
中级会员
Rank: 3Rank: 3
中级会员



UID 29240
精华 4
积分 402
帖子 322
金钱 361 喜悦币
威望 40
人脉 1
阅读权限 30
注册 2004-6-8
来自 北京
状态 离线
[推荐阅读] 请教高手,急!!!
使用PHPSA搭建你的应用程序(phpsa系列教程之一)

使用PHPSA搭建你的应用程序(phpsa系列教程之一)
大师兄(teacherli(-at-)gmail.com) 2005-12-16

            从本节开始, 我将以一种实战例程的方式来讲解一下如何将PHPSA应用到你的开发中, 我们的例子选择自一个学校的网站, 主要的功能将在以后的教程中逐渐加入, 本篇中先来讲解一下如何使用PHPSA搭建这个学校网站环境.
       
        PHPSA需要php5支持, 如果你的当前PHP版本是4系列, 请升级为PHP5后再进行测试; 如果使用UTF字符编码的话mysql要使用4.1以上, 使用其它语言编码无此要求.
       
        下载PHPSA, 解压phpsa.rar至e:\phpsa, 将解压后的目录名称更改为fecollege, 在apache中将DocumentRoot设置为e:\fecollege目录, 修改e:\fecollege\comm\config\config.class.php配置文件, 主要修改的参数为:
        1. const APPLICATION_DIR = "e:/fecollege":         主程序目录
        2. const DB_TYPE = "mysql";                  数据库类型
        3. const DB_HOST = "localhost";                数据服务器
        4. const DB_NAME = "fecollege";                数据名称
        5. const DB_USER = "root";                数据库用户名
        6. const DB_PASSWORD = "";                连接数据库密码
       
        重启apache,打开你的浏览器, 输入http://localhost/index.php, 如果能够正常的出现在PHPSA测试实例,说明你的程序搭建完成.


        在PHPSA中搭建应用程序, 实际上是把你的应用程序先按模块(module)划分, 在模块中再划分各类与模块相关的动作(action),然后通过HTML链接来完成对相关动作的调用来实现程序功能. 例如一个用户注册模块, module名为userReg, 实现用户注册功能可以下分为以下几个action:
        1. regAction: 显示用户注册页面/执行注册操作/错误提示
        2. checkUserNameAction: 检测用户名是否被占用
        3. regSuccessAction: 显示注册成功
        regAction主要的功能有两个, 一个是显示注册界面, 包含用户第一次打开注册页面及注册表单后经过form类验证后出错返回提示; 另一个为提交用户注册信息表单. 这里就要分别实现regAction类的doView()函数与doAction()函数, 在doView()中处理与页面显示相关的东西, 在doAction中实现用户提交表单后的入库操作.
        checkUserNameAction主要用户检测用户名是否被占用
        regSuccessAction:用户执行regAction成功后使用return new Forward("user!regSuccess")跳转至此action, 用来提示用户已经注册成功
       
        需要讲明的, 这里的模块及模块的动作的划分应以你的程序为主, 划分的方法也是多种多样的, 例如上边的这个例子, 如果想用户注册时先输入用户名, 然后提交检测, 数据库中不存在相同的用户名时进行下一步详细信息填写, 这个流程中使用到的动作划分可以如下:
        1. reg1Action: 显示输入用户名/提交后检测用户是否存在
        2. reg2Action: 用户其它详细信息/提交数据库
        3. regSuccessAction:用户注册成功后的提示页面
       
        这里需要注意: 每个module名称与每个action名称的首字母都要为小写.

        让我们继续搭建我们的例程, 我们希望打开http://locahost/index.php后出现的不是框架默认的那个测试页面, 而是一个学校的首页, 同时添加一个"在线报名"的链接.
默认模块我们定义为default, 显示首页的action定义为defaultAction. 程序中所有自定义的模块都要放置在modules目录中, 首先删除系统中的测试模块default, 然后在此目录中加入一个新的文件夹default, 按照模板的定义规范, 我们分别在新创建的default目录中加入forms, actions, views, model目录, 4个目录的作用我在框架附带的说明中已经说过, forms存放POST表单验证类, actions存放模板中定义的各种方法, views目录存放各类模板, model目录存放业务逻辑, 现在让我们在actions目录中编写defaultAction.class.php文件:

<?php
 
/**
  * default action
  * 
  * fecollege index page
  * 
  * @author teacherli 2005-11-2
  */
  
class DefaultAction extends Action {
      
      public function 
DefaultAction($form null) {
          
parent::Action($form);
      }
      
    
      public function 
doView() {
          
$this->view->setTplDir("./modules/default/views");
          
$this->view->setTpl("index.html");
        
        
$this->view->assign("title""phpsa框架示例");        

          
$this->view->display();
      }
  }
?>
所有的动作必需继承自Action类, 有对象的form类的时请重写子类的构造函数, 这里的构造函数采用JAVA风格,使用与类名相同的的函数, 函数函数接授一个Form类的变量, 在构造函数中调用父类的构造函数并将Form类变量传递过去. 根据action所完成功能不同它可以分为两类:一类为显示界面, 另一类为执行一个操作, 本例中我们只需要它实现显示界面这个功能, 因此我们重写Action类的doView方法即可.
        doView()方法实现的功能主要由封装后的smarty来完成, 成员变量$view已经在Action类中定义, 此处直接拿来使用, 其中最重要的有这三条语句:
        1. $this->view->setTplDir("./modules/(moduleName)/views"): 设置动作所对应的模板目录
        2. $this->view->setTpl("index.html"): 设置动作所要执行的模板名称
        3. $this->view->display(): 显示action界面
        这三条语句是一个doView()函数一般进行显示界面操作的语句, 它组成doView()函数的基本功能.
        4. $this->view->assign("title", "phpsa框架示例"); 熟悉Smarty的朋友可能已经猜出意思来了,就是为模板变量赋一个值, 模板变更为title, 值为"phpsas框架示例"

        来看views目录中的index.html模板(注意: 这里模板扩展名我选择.html, 模板中所用到的界定符为<%%>)

<?php
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<
html>
  <
head>
    <
title><%$title%></title>
    <
meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </
head>
  
  <
body>
      <
a href="?action=signup!signup">报名系统</a>
  </
body>
</
html>
?>
模板采用smarty的标准语法, 不了解smarty的读者自行去查看smarty的相关教程.
        在模板中我们使用了一个链接<a href="?action=signup!signup">报名系统</a>, 这是一个基本的HTML链接元素, 指向在线报名模块的signup动作, 在线报名模块我们下一节再进行讲解. 好了, 关于PHPSA框架的安装使用就讲到这里了, 使用过程中有任何问题,欢迎发邮件与我共同讨论.
2005-12-20 11:55 PM#3
查看资料  发短消息  QQ . .   顶部
 
teacherli (teacherli)
中级会员
Rank: 3Rank: 3
中级会员



UID 29240
精华 4
积分 402
帖子 322
金钱 361 喜悦币
威望 40
人脉 1
阅读权限 30
注册 2004-6-8
来自 北京
状态 离线
[推荐阅读] 能把汉字转化为拼音的一个函数
使用Form类进行表单字段验证 (phpsa系列教程之二)

使用Form类进行表单字段验证 (phpsa系列教程之二)
大师兄(teacherli(-at-)gmail.com) 2005-12-17

        上一节讲了如何使用PHPSA搭建一个开发环境, 这节来说一下如果在PHPSA中使用FORM类进行表单验证. PHPSA中的Form与Action类对应, 主要的功能为进行以POST提交的表单字段进行服务器端验证, 通过预定义在comm/utils/validator.class.php中的各类逻辑方法进行验证. 在PHPSA中,当提交后验证表单出错后程序可以自动返回至表单, 保留用户录入数据并显示错误信息.

        继续拿我们上节课的例子进行讲解. 上节课的最后我们定义了一个链接<a href="?action=signup!signup">在线报名</a>,
从这个链接中我们可以看出, 有另外一个模块:signup, 模块中有一个Action叫signupAction, OK, 让我们先来在程序中建立一个signup模块: 在modules目录下建立一个signup目录, 分别在modules/signup/下建立forms, actions, views, model四个目录, 在actions目录新建立一个文件叫signupAction.class.php, 在forms目录中新建一个文件名叫signupForm.class.php与前边我们建立的action类对应.
        先来说明一下我们的signup模块的功能: signup主要提供在线报名功能, 其中可以分为两个Action, 其中一个为signupAction, 主要提供: 1. 显示在线报名表单 2.提交报名信息; 另一个为successAction, 提交用户信息成功后跳转至另一个页面, 告诉用户已经成功报名.
        signupAction上边我们已经建立, successAction主要用来显示一个提示页面, 因此不需要定义对应的Form类,我们直接在modules/signup/actions目录中再加个一个新的文件successAction.class.php.
        对了,因为我们新加入了模块, 因此我们需要在comm/config/guest.config.xml文件中定义这个模块及模块的Action, 添加如下内容:

<?php
<module name="signup">
        <
action>signup</action>
        <
action>success</action>
    </
module>
?>
其中, module项的name属性为新加入的模块名称, <action>[actionName]</action>为模块中对应的Action名称, 加入我们刚才定义的两个action.

        OK, 准备工作已经完成.下面来看一下在线报名需要添加的字段内容:
        1. 用户名:         text
        2. 性别:        radio
        3. 身份证号:        text
        4. 民族:        select       
        5. 文化程度:        select
        6. 政治面貌:        radio
        7. 籍贯:        select
        8. 邮政编码:        text
        9. 通信地址:        text
        10. 电话:        text
        11. 手机:        text       
        12. email:        text
        13. 报考专业:        select
        14. 学习阶段:        radio
        在这其中, 报考专业由数据库中的专业表中提取,其它信息为静态提供. 来看我们的模板文件(modules/signup/views/signup.html)中的代码片段:

代码片段1:

<?php
<input type="text" name="userName" value="<%$userName%>">
?>
为能在表单验证出错后返回时还能保存用户曾经输入过的值, 我们给每个text类型的字段加入value属性, value属性的值为一个模板变量, 变量名称为text字段名称. 这里有个注意的地方:所有的表单字段都应使用小写字母开始的变量名.

代码片段2:

<?php
<td>性别:</td>
    <
td>
    <%if 
=== $sex%>
       <%
assign var="sexChecked0" value="checked"%>
    <%else%>
        <%
assign var="sexChecked1" value="checked"%>
    <%/if%>
    <
input name="sex" type="radio" value="1" <%$sexChecked1%>>
      

      
<input type="radio" name="sex" value="0" <%$sexChecked0%>></td>
?>
对于radio字段如何在验证出错后保持它的提交状态, 需要使用模板流程控制语句来处理. 在这里, 男使用标准代码1来代表, 女使用0来代表, 在每个radio字段后边加入了<%$sexCheckedX%>类似于这样的模板变量, 它的定义模式为字段名 + Checked + 当前字段值这样的方式, 然后通过<%if%><%else%>等语句来对当前的字段值进行判断, 进行处理相应的模板变量,使radio字串呈选中状态.

代码片断3:

<?php
<td>文化程度:</td>
    <
td>
    <% if 
"" == $nation%>
        <%
assign var="eduSelected4" value="selected"%>
    <% else %>
        <%
assign var="eduSelected$edu" value="selected"%>
    <%/if%>
    <
select name="edu">
    <
option value="">请选择  </option>
                      <
option value="1" <%$eduSelected1%>>博士</option>
                      <
option value="2" <%$eduSelected2%>>硕士</option>
                      <
option value="3" <%$eduSelected3%>>大学</option>
                      <
option value="4" <%$eduSelected4%>>高中</option>
                      <
option value="5" <%$eduSelected5%>>初中</option>
                      <
option value="6" <%$eduSelected6%>>小学</option>
    </
select></td>
?>
对于select字段同样采用模板流程控制语句, 方法与radio字段差不多, 考虑到默认选项, 也就是当第一次打开模板页面时$nation变量无值, 这时就要加入一个 if "" == $nation的条件语句来处理默认选中的字段.


代码片段4:

<?php
<div style="color:red; font-size:12px; padding:5px;" align="center"> <%showErrors%></div>
?>
这一段中使用了一个模板方法(注意:这里是模板方法不是模板变量,不要在前边加$符)<%showErrors%>, 它为自定义的一个smarty插件, 主要用来显示错误信息. 它有另外一个方法: <%showErrors var="xxxError"%>, 带一个参数表示取当前错误, 值定义在Form的继承类中. 一般情况直接使用<%showErrors%>打印出所有的错误信息.

        好了,模板中内容就讲到这里, 下面来看SignupForm类:

<?php
/**
 * signup form
 * @author 大师兄
 * @copyright Copyright &copy; 2005-12-14 
 */
class SignupForm extends Form {

    public function 
SignupForm() {
        
parent::Form();
        
$this->validate true;
    }
    
    
/**
     * implements validate method
     * @return ActionMessage
     */
    
public function validate() {
        
$actionMessage = new ActionMessage();
        
        if (
Validator::isEmpty($this->getUserName())) {
            
$actionMessage->setErrorsValue("userNameError""用户名不能为空!");
        } else if (!
Validator::isLength($this->getCardNum(), 15)) {
            
$actionMessage->setErrorsValue("cardNumError""身份证号码不正确!");
        } else if (!
Validator::isNumber($this->getPostcode()) || !Validator::isLength($this->getPostcode(), 6)) {
            
$actionMessage->setErrorsValue("postcodeError""邮政编码不正确!!");
        } else if (
Validator::isEmpty($this->getAddress())) {
            
$actionMessage->setErrorsValue("addressError""通信地址不能为空!");        
        } else if (
Validator::isEqual($this->getSpec(), "0")) {
            
$actionMessage->setErrorsValue("specError""请选择报考专业!");
        }
        
        return 
$actionMessage;
    }
}
?>
SignupForm继承自Form类, 注意, 编写Form类的继承类时请加入继承类的构造函数, 构造函数中有两行,第一行调用Form类的构造函数; 第二行为是否对表单进行验证, 此变量设置为true时请重写validate()方法.
        下面重点来看看public function validate()内容:
         首先申明一个ActionMessage类型变量, 此变量中保存所有对表单进行验证的信息. 接下来使用Validator类对表单各类需要进行验证的字段进行验证, 验证的方式类似于:

<?php
Validator
::isEmpty($this->getUserName())
?>
, 这里Validator::isEmpty($var)定义自comm/utils/validator.class.php, 功能为验证变量是否为空; $this->getUserName()使用PHP5中的魔术函数来实现, $this->getUserName()相当于调用$this->userName; 而$this->userName又来自于$_POST["userName"]即表单字段, 关于Validator中定义的各类表单验证方法请查询框架中的说明文档.
        最后validate()函数返回一个ActionMessasge对象, 系统根据返回ActionMessage是否保存有错误信息来进行下一步处理: 如果存在错误信息, 则返回表单页, 否则交由Action类的doAction()方法进行下一步处理.

        最后来看看SignupAction类中的内容:

<?php
/**
 * sigup action
 * @author 大师兄 2005-12-17
 */
class SignupAction extends Action {
    
    public function 
SignupAction($form null) {
        
parent::Action($form);
    }
    
    public function 
doView() {
        
$this->view->setTplDir("./modules/signup/views");
      
$this->view->setTpl("signup.html");
            
      
$this->view->display();
    }
    
    public function 
doAction() {
        echo 
"晢时不实现此功能, 下一节讲如何设计业务逻辑及实现业务逻辑验证方法!";
    }
}
?>
同样, 与defaultAction类似, 我们为SignupAction编写了构造方法, 实现了Action的两个函数doView()与doAction(), doView()主要用来显示填写注册信息页面, 本节只对doView()方法实现, doAction()方法只做一个简单的演示, 留着下一节讲业务逻辑及业务逻辑验证讲.

        好了, 关于PHPSA框架的Form表单验证就讲到这里了, 学习与使用过程中有任何问题,欢迎发邮件与我共同讨论.
2005-12-20 11:55 PM#4
查看资料  发短消息  QQ . .   顶部
 
teacherli (teacherli)
中级会员
Rank: 3Rank: 3
中级会员



UID 29240
精华 4
积分 402
帖子 322
金钱 361 喜悦币
威望 40
人脉 1
阅读权限 30
注册 2004-6-8
来自 北京
状态 离线
[推荐阅读] tu
编写你的业务逻辑与实现逻辑验证 (phpsa系列教程之三)

编写你的业务逻辑与实现逻辑验证 (phpsa系列教程之三)
大师兄(teacherli(-at-)gmail.com) 2005-12-18

        上一次我们把在线报名的界面做完了, 而实际的功能我们留下没讲, 这一讲我们来看看如何在doAction()方法中调用业务逻辑类来完成我们的用户在线报名注册.

        PHPSA中以模块做为编程单位, 每一模块中又可以按照层次可分为forms, actions, model, views, 分别代表显示层, 控制层与逻辑层, 从文件夹的名称上, 我们可以看了在PHPSA中推荐在每一模块中只实现一个业务逻辑类, 通过这一个公用的业务逻辑类, 完成模块中所有Action的相关逻辑操作.
        首先来看看这个例子中我们主要用到的数据表定义:

<?php
/*==============================================================*/
/* Table: tb_signup_user                                        */
/*==============================================================*/
create table tb_signup_user
(
   
i_id                           int unsigned                   not null    auto_increment,
   
vc_user_name                   varchar(20),
   
i_sex                          tinyint,
   
vc_card_num                    varchar(18),
   
im_notion_id                   tinyint unsigned,
   
im_edu_id                      tinyint unsigned,
   
im_polity_id                   tinyint unsigned,
   
im_prov_id                     tinyint unsigned,
   
c_post_code                    char(6),
   
vc_address                     varchar(150),
   
vc_tel                         varchar(15),
   
vc_m_tel                       varchar(15),
   
vc_email                       varchar(50),
   
im_spec_id                     int unsigned,
   
primary key (i_id)
ENGINE=InnoDB DEFAULT CHARSET=utf8;    

/*==============================================================*/
/* Table: tb_spec                                               */
/*==============================================================*/
create table tb_spec
(
   
i_id                           int unsigned                   not null    auto_increment,
   
vc_name                        varchar(100),
   
im_college_id                  tinyint unsigned,
   
primary key (i_id)
ENGINE=InnoDB DEFAULT CHARSET=utf8;
?>
以signup模块为代表,来看一下如何编写一个业务逻辑处理类. 在./modules/signup/model/目录中新建立一个类文件, 名称为: signupBusinessService.class.php, 这个文件的命名方式为: 模块名(小写字母开始) + "BusinessServic.class.php",  而类名则将模块名的首字母大小, 像这样:
class SignupBusinessService extends BaseBusinessService {...}.
来看看signupBusinessService.class.php文件的代码:

<?php
/**
 * signup model
 * @author teacherli 2005-12-17
 */
require_once (Config::APPLICATION_DIR "/comm/model/baseBusinessService.class.php");

class 
SignupBusinessService extends BaseBusinessService {
    
    
/**
     * return spec list
     * @return 2 dim array
     */
    
public function getSpecList() {
    }
    
    
/**
     * insert signup info to database
     * @param Form $signupInfo
     * @return boolean
     */
    
public function insertSignupInfo($signupInfo) {
    }
    
    
/**
     * check if cardnum exist
     * @param String $cardNum
     * @return boolean if exist return true else return false
     */
    
public function checkCardNum($cardNum) {
    }
}
?>
SignupBusinessService继承自BaseBusinessService类, 通过定义成员函数来实现逻辑功能的实现, 来看getSpecList()函数代码:

<?php
/**
     * return spec list
     * @return 2 dim array
     */
    
public function getSpecList() {
        
$strQuery "SELECT i_id AS id, vc_name AS name " .
                   
"FROM tb_spec";
        
        return 
$this->db->getList($strQuery);
    }
?>
函数定义了一个SQL查询语句, 通过组合的重新封装的adodb类按照二维数组的方式查询出所有的专业列表. adodb类经过重新封装后只能使用如下几个函数:

<?php
6.1  DB
(): 构造函数负责完成数据库连接
6.2  getOne
($strQuery): 取得查询返回值第一行的第一列值
6.3  getList
($strQuery$errorMessage="can't call adodb's GetAll, check your sql sentence!"): 取得查询返回所有记录
6.4  getLimitList
($strQuery$pageNum$pageCount$errorMessage): 分页查询返回
        $strQuery
查询SQL语句
        $pageNum
页数
        $pageCount
每页记录数
6.5  getRow
($strQuery): 返回查询第一行记录组成的数组
6.6  getLastInsertID
(): 返回上次插入的自动增值类型记录值由于存在多并发下些记录访问的问题请谨慎使用此函数.
6.7  update($strQuery): 执行更新语句调用execute
6.8  modify
($strQuery): 执行修改语句调用execute
6.9  delete
($strQuery): 执行删除语句调用excute
6.10  insert
($strQuery): 执行插入语句调用excute
6.11  quotes
($var): 对指定变量进行quotes参数为字符串或数组
6.12  close
():  关闭数据库连接
?>
封装ADODB的主要目的就是为了明确逻辑功能, 降低程序复杂度.
        回过头来看看我们的signupAction.class.php文件中的doView()函数, doView()函数用来显示一个供用户填写信息的界面, 在这个界面中,需要从数据库里将所有专业提取出来后供用户进行选择, 由数据库里提取专业列表就由此SignupBusinessService类的getSpecList()来完成, 看我们修改后的doView()函数:

<?php
/**
     * implements doView
     */
    
public function doView() {
        
$this->view->setTplDir("./modules/signup/views");
          
$this->view->setTpl("signup.html");
        
        
//调用业务逻辑由数据库中提取专业例表
        
$specList $this->signupBusinessService->getSpecList();
        
$this->view->assign("specList"$specList);
        unset(
$specList);
        
          
$this->view->display();
    }
?>
$specList为由数据库里提取一个二维数组, 然后交由view进行处理,显示在供用户进行选择专业的下拉列表中, $this->singupBusinessService在SignupBusinessService类的构造函数中进行实例化.

        来看我们的SignupBusinessService类的doAction()方法, 当用户提交表单后经过SignupForm类验证无误后交由此方法进行执行具体的入库操作, 现在我们又有一个要求:要求用户提供的注册信息中的身份证号码不能重复, 也就是一个用户不可进行多次报名, 很显然, 这要涉及到操作数据库, 在Form中的字段验证就没有办法进行处理这样的验证, 因而我们在这里引用了"逻辑验证"方式.
        "逻辑验证"指的是在数据入库前由业务逻辑模块对数据字段的有效性进行检验的方式, 来看我们在SignupBusinessService类中定义的要进行逻辑校验的方法代码:

<?php
/**
     * check if cardnum exist
     * @param String $cardNum
     * @return boolean if exist return true else return false
     */
    
public function checkCardNum($cardNum) {
        
$strQuery "SELECT COUNT(*) " .
                
"FROM tb_signup_user " .
                
"WHERE vc_card_num = '" $cardNum "'";
                    
        return (
!= $this->db->getOne($strQuery));
    }
?>
函数接受一个$cardNum的参数, 交由SQL语句进行数据库验证, 如果数据库中已经存在这个身份证号码, 则返回true, 否则返回一个false值, 此函数由SignupAction类的doAction()方法进行调用, SignupAction类的doAction()方法代码如下所示:

<?php
/**
     * implements doAction
     */
    
public function doAction() {
        
//执行逻辑验证
        
if ($signupBusinessService->checkCardNum($this->form->getCardNum())) {
            
$actionMessage = new ActionMessage();
            
$actionMessage->setErrorsValue("cardNumError""对不起, 您的身份证号已经存在于数据库中, 请不要重复报名!");
            
            return 
$actionMessage;
        }
        
    
//执行插入
        
if ($this->signupBusinessService->insertSignupInfo($this->form)) {
            return new 
Forward("signup!success""报名资料已经提交, 请等待...");
        }
    }
?>
此方法由两部分组成, 第一部分为进行逻辑验证, 第二部分将用户填写的信息插入数据库, 先来分析在doAction()中执行的逻辑验证代码:

<?php
if ($signupBusinessService->checkCardNum($this->form->getCardNum())) {
            
$actionMessage = new ActionMessage();
            
$actionMessage->setErrorsValue("cardNumError""对不起, 您的身份证号已经存在于数据库中, 请不要重复报名!");
            
            return 
$actionMessage;
        }
?>
SignupBusinessServie类的checkCardNum()方法在上边我已经介绍过, 现在重点来介绍一个ActionMessage类, 按程序的流程, 如果发现在在数据库中存在传入的指定身份证号码, 则首先实例化一个ActionMessage类, 然后给调用setErrorsValue()方法对ActionMessage设定一条错误信息, 接着将这个ActionMessasge实例返回. Application类调用doAction()函数返回的这个ActionMessage类, 查找其中的错误信息后返回执行Action的doView()函数, 并将错误信息显示在用户界面上. 关于ActionMessage的详细信息, 请参阅PHPSA框架跌phpsa framework.doc文件.
        接下来调用业务逻辑处理类的insertSignupInfo()函数执行对用户表单信息的入库操作, 如果成功的插入, 返回给Application类一个Forward类, 此类负责对URL进行重定义, 重定向过程中调用另一个页面, 并在页面中显示Forward类的构造函数的第二个参数中定义的字串. 如果想直接跳转, 请返回Redirect($url)类, $url为返回的action字串.
        看完逻辑代码, 大家就可能明白为什么我要新设计一个Form类, 此类封装POST表单字段, 在Action类与BusinessService类之间扮演着DTO的角色, 因此, 在有POST进行提交的表单中, 大家一定要记的重新生成一个Form子类, 否则无法在Action与BusinessService类之间进行数据传送. 当然,有人说我可以传$_POST? 但考虑数据封装及可能进行的表单验证, 请大家重新生成一个Form的子类做作为DTO进行数据传递.
       
        现在来总结一下module, Action, Form, BusinessService类分别在程序中的作用:
        module: 指的是程序的功能单位, 在进行程序设计时,要程序按功能划分为不同的子程序, 这里子程序的概念就是这里module的概念, 代表组成程序的功能单位.
        Action: 在module中实现某一功能的最小程序单元, 它实现了显示界面、执行操作两种方法, 在实际的程序中我们只需要重新实现这两个方法来实现相应的功能.记住:在Action担当的控制器(Controller)的功能, 它通过调用BusinessService中的公用接口函数来完成界面的显示及数据的提交.
        Form:        在有POST表单字段提交的程序中担任着:1.表单字段服务器端校验 2.作为DTO在Action与BusinessService之间传递表单数据.
        BusinessService:用来处理模块中所有的业务逻辑, 通过公用接口的方式为Action类提供函数调用来实现逻辑处理, 所有的与数据库进行交互的活动都应封装在些类中.
        程序的执行过程请参照phpsa framework.doc文档.
       
        好了, 关于业务逻辑及进行逻辑验证这部分就讲到这里了... 学习与使用过程中有任何问题,欢迎发邮件与我共同讨论.
2005-12-20 11:56 PM#5
查看资料  发短消息  QQ . .   顶部
 
teacherli (teacherli)
中级会员
Rank: 3Rank: 3
中级会员



UID 29240
精华 4
积分 402
帖子 322
金钱 361 喜悦币
威望 40
人脉 1
阅读权限 30
注册 2004-6-8
来自 北京
状态 离线
[推荐阅读] 那位有Socket的资料呢?
PHPSA中实现缓存方案 (phpsa系列教程之五)

PHPSA中实现缓存方案 (phpsa系列教程之五)
大师兄(teacherli(-at-)gmail.com) 2005-12-19

        前几节将PHPSA的一些基本的用法给大家介绍了一下, 这节来看看如果在PHPSA中使用缓存方案. 我们知道, 缓存的级别有两种, 一种为对页面进行缓存, 另一种对数据库提取数据进行缓存, 我们这里主要考虑对页面的缓存方案. 页面缓存基本的原理就是将页面内容缓存到文件, 当第一次缓存后, 在缓存的有效时间内再次请求同一URL时将直接由缓存文件中调出数据, 生成静态页实际就是一种典型的缓存机制, 不过它缓存后会由用户直接请求, 而不再经过PHP进行缓存内容的提取显示. 现在很多的论坛都采用缓存技术来应对大并发用户请求, 它的方案就类似我现在讲的这种方案, 将页面内容先缓存到文件中, 在用户下次进行请求时程序不进行数据库查询而是直接由文件中取出已经缓存的页面内容.
        举一个简单的例子: http://abc.com/index.php, index.php文件为首页, 里边包含着大量的新闻性数据, 新闻性的数据又分为很多的子类, 这样, 要想显示给用户完整体的一个首页, index.php中就要对数据库进行多次请求, 这里设定为N次, 而网站又同时并发访问M个用户, 这样, 在单位时间内程序就需要执行M次数据库连接N*M次数据请求, 考虑到首页主要显示一些动态性不高的新闻, 这时我们就可以使用缓存到文件的办法, 在第一次执行index.php时取得index.php显示给用户的完整页面内容, 然后将它写入一个缓存文件index.cache, 在缓存的有效时间内用户再次访问index.php的时候程序使用文件读写方式由index.cache文件中取出所有内容, 然后显示给用户.
        PHPSA框架将显示与动作分离, 通过集成的缓存类Cache可以很轻松的完成这种缓存方案, 先来看看Cache类中主要定义的方法:

<?php
14.1  getCacheID
(): 获取cacheID静态调用
14.2  display
($cacheID): 显示$cacheID缓存内容$cacheID为缓存块ID静态调用
14.3  fetch
($cacheID$display=false): 获取缓存块内容$cacheID为缓存ID,$display为是否获取同时进行显示, 静态调用
14.4  isValid
($cacheID): 检测$cacheID缓存内容是否有效, 静态调用
14.5  cachePage
($cacheID$contents): 缓存静态页内容, 静态调用
?>
Cache类所有的方法都为静态方法, 程序中我们可以使用Cache::methodName()的方式进行调用.
        缓存主要是对于程序的界面进行缓存, 因而我们要在Action类的doView()方法中实现缓存, 来看defaultAction.class.php中的代码:

<?php
 
/**
  * default action
  * 
  * fecollege index page
  * 
  * @author teacherlxj 2005-12-19
  */
  
class DefaultAction extends Action {
      
      public function 
DefaultAction($form null) {
          
parent::Action($form);
      }
      
    
//show default page
      
public function doView() {
          
/**
           * 这里要进行页面缓存要将config.class.php中的CACHE_TIME设置为一个有效的秒数缓存才会生效 
           */
          
          
ClassLoader::loadUtilsClass("cache");
          
//cacheID根据URL进行取值, 每个URL对应一个缓存ID
          
$cacheID Cache::getCacheID();

        if (
Cache::isValid($cacheID)){//如果已经缓存且还在缓存时间内
            
Cache::display($cacheID);
        } else { 
//没有缓存或是已经在缓存时间外
            
$this->view->setTplDir("./modules/default/views");
              
$this->view->setTpl("index.html");
        
            
ClassLoader::loadModelClass("default");
            
$defaultBusinessService = new DefaultBusinessService();
        
            
//学院信息类新闻 typeID = 1, Config::DEFAULT_NEWS_COUNT在config.class.php中定义
            
$collegeNewsList $defaultBusinessService->getNewsList(1Config::DEFAULT_NEWS_COUNT);
            
$this->view->assign("collegeNewsList"$collegeNewsList);
            unset(
$collegeNewsList);
        
            
//教学教务新闻 typeID = 2
            
$techNewsList $defaultBusinessService->getNewsList(2Config::DEFAULT_NEWS_COUNT);
            
$this->view->assign("techNewsList"$techNewsList);
            unset(
$techNewsList);
        
            
//招生新闻 typeID=3
            
$recruitNewsList $defaultBusinessService->getNewsList(3Config::DEFAULT_NEWS_COUNT);
            
$this->view->assign("recruitNewsList"$recruitNewsList);
            unset(
$recruitNewsList);
        
            
//远程信息新闻 typeID =4
            
$remoteNewsList $defaultBusinessService->getNewsList(4Config::DEFAULT_NEWS_COUNT);
            
$this->view->assign("remoteNewsList"$remoteNewsList);
            unset(
$remoteNewsList);
            
            
//获取当前页面内容, 写入缓存
              
$contents $this->view->fetch();
            
Cache::cachePage($cacheID$contents);

            
$this->view->display();
        }
      }
  }
?>
doView()方法中实现的缓存方案. 首先要加载Cache类, 然后使用Cache::getCacheID()获取一个缓存ID号, 缓存ID由URL经过MD5进行加密,然后使用Base64进行编码, 它作为缓存的文件名. 所有缓存文件都被放入Config::APPLICATION_DIR . "/cache"目录, 因此我们在LINUX下使用缓存方案时要注意此目录的存取权限. 接下来使用Cache::isValid($cacheID)来判断缓存是否有效, 这里的有效根据两点进行判断: 1.缓存文件是否存在 2.缓存文件是否还在缓存有效期内, 如果缓存文件有效, 则使用Cache::display($cacheID)方法直接显示缓存文件中的内容, 否则继续执行正常的数据库查询工作. 同样, 按正常的流程, 设定模板目录, 设定模板文件名, 处理模板变量, 在最后, 通过View类的fetch()方法将已经处理完成的模板内容返回,然后将它写入缓存文件, 最后执行模板显示. 流程很简单, 当下次再有用户请求时, Cache::isValid($cacheID)将返回一个true值, 直接由缓存文件中取出页面内容并进行显示.
        注意, 不要将缓存机制放入doAction()方法中实现, 原因就不用说了吧?

        好了, 关于缓存方案这部分就讲到这里了... 学习与使用过程中有任何问题,欢迎发邮件与我共同讨论.
2005-12-20 11:57 PM#6
查看资料  发短消息  QQ . .   顶部
 
teacherli (teacherli)
中级会员
Rank: 3Rank: 3
中级会员



UID 29240
精华 4
积分 402
帖子 322
金钱 361 喜悦币
威望 40
人脉 1
阅读权限 30
注册 2004-6-8
来自 北京
状态 离线
[推荐阅读] Aeder 表格通用添加编辑删除基础模块
PHPSA中的实用类之分页篇 (phpsa系列教程之六)

PHPSA中的实用类之分页篇 (phpsa系列教程之六)
大师兄(teacherli(-at-)gmail.com) 2005-12-20

        PHPSA中集成了分页字串处理类, 这一节我们来简单的介绍一下如何在你的程序中使用分页字串处理类. 分页主要应用于列表显示, 为演示这个功能, 我们在示例程序default模块中加入listAction, 用来对首页中四大块新闻点击"更多新闻"链接时的处理动作. 同样, 首先在modules/default/actions目录下建立listAction.class.php文件, 然后在comm/config/guest.config.xml文件中加入<action>list</action>语句, 为访问者设定执行权限.
        分页表现在页面中主要是提供了一个可供用户选择页面的分页字串, 先来看看模板中的内容:

<?php
<table width="80%" border="1" align="center">
    <
tr>
      <
td><strong style="font-size:36px;"><%$typeName%></strong></td>
    </
tr>
    <%
section name=loop loop=$newsList%>
    <
tr  bgcolor="<%cycle values="#9999CC,#DEDEDE"%>">
      <