且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

《Android和PHP开发***实践 》一3.6 开发框架简介

更新时间:2022-09-29 20:42:20

3.6 开发框架简介

前面大家已经学习了PHP模板引擎Smarty的用法,也简单了解了PHP的官方框架Zend Framework,接下来本书将给大家介绍一个基于Zend Framework和Smarty之上的强大的PHP开发框架,即Hush Framework。本书后面微博实例的服务端程序也将采用该框架进行开发。
在实际项目中,我们通常要先选择一个比较适合项目特点的框架,然后,在这个框架的基础上进行开发,这个过程我们通常称为“框架选型”。其实,在之前的3.4节中我们已经介绍和分析了四种目前比较主流的PHP框架,并选定了Zend Framework为本书实例的基础框架,我们通过长时间的整理归纳和项目积累,在Zend Framework和Smarty的基础上构建出了Hush Framework这个PHP的开发框架,个人还是非常推荐大家使用的,接下来我们从几个方面给大家介绍一下这个框架。

3.6.1 框架的特点和优势

从某种程度上来说Hush Framework框架的特点也可以算做它的优势,所以我们主要给大家列举该框架的几个主要特点。

  1. 开发效率高
    Hush Framework基本沿用了Zend Framework严谨的编码规范和优秀的框架设计,形成了别具特色的MVC结构;此外,本框架目前已经被国内多个知名网络公司所采用,经过了多个实际项目的考验和提炼已经日渐成熟;另外,此框架相对比较适合国内程序员的思路,从而提高了开发效率。
  2. 运行效率高
    虽然Zend Framework的运行效率一直为大家所诟病,但是其中最重要的原因是类库实在太庞大了,因此Hush Framework只使用了其中几个核心类库,并对其中一些效率不够高的地方进行了精简和优化,比如URL的路由逻辑优化,DB类的用法简化等,极大限度地提高了整个框架的运行效率。
  3. 可扩展性高
    Hush Framework的高扩展性得益于Zend Framework优秀的类库设计,松耦合的设计方法让它能快速地适应不同项目的需求,这也是为什么我们会选择Hush Framework来作为本书实例的服务端底层框架的最主要原因之一。

当然,除了以上这些基础特点之外,Hush Framework还有很多其他的很棒的特性,比如我们可以用它的基础代码来快速开发一个常见的互联网应用,还可以用其自带的工作流模块来开发ERP系统等,但是由于本书实例仅用到框架中的最基础的MVC代码框架,所以我们后面的介绍也将围绕着Hush Framework的基本用法来给大家讲解,其中我们也会穿插一些PHP编程的要点,让大家更加了解如何在实际项目中使用PHP语言来编程。

3.6.2 框架的基础目录结构

想要熟悉一个框架,***的方式莫过于从它的代码目录结构入手,下面我们先来讲解一下Hush Framework的基础目录结构,让大家对这个框架有一个整体性的认识。下面便是对这个框架主要目录的一个对照,我建议大家使用svn工具到Hush Framework的官方网站(http://code.google.com/p/hush-framework/)上把代码下载到本地来进行比对阅读,这样才会达到比较好的学习效果。
目录说明 3-1

hush-framework
|
|- hush-app                    实例应用程序目录
|  |- bin                        可执行文件目录
|  |- dat                        临时存储文件
|  |- doc                        主要文档目录
|  |- etc                        配置文件目录
|  |- lib                        主要逻辑目录
|  |  |- Ihush
|  |     |- Acl                    ACL 权限逻辑类库
|  |     |- App
|  |     |  |- Backend
|  |     |  |  |- Page                后台 Controller 逻辑
|  |     |  |  |- Remote                后台 Service 逻辑
|  |     |  |- Frontend
|  |     |     |- Page                前台 Controller 逻辑
|  |     |- Bpm                    Bpm 逻辑类库
|  |     |- Dao
|  |        |- Apps                Apps 库的 Module/Dao 类库
|  |        |- Core                Core 库的 Module/Dao 类库
|  |- tpl
|  |  |- backend                    后台模板文件
|  |  |- frontend                    前台模板文件
|  |- web
|     |- backend                    后台 DocumentRoot(站点目录)
|     |- frontend                    前台 DocumentRoot(站点目录)
|
|- hush-lib
|  |- Hush
|     |- Acl                    Acl 权限类库
|     |- App                    App Url Dispatcher
|     |- Auth
|     |- Bpm                    Bpm 类库
|     |- Cache                    Cache 类库
|     |- Chart                    图像类库
|     |- Crypt                    加密类(Rsa)
|     |- Date
|     |- Db                    数据库层(Module)类库
|     |- Debug                    调试类库
|     |- Document                    文档类库
|     |- Examples                    一些例子(主要针对 Cli 程序)
|     |- Html                    Html 构建类库
|     |- Http                    远程访问类库
|     |- Json
|     |- Mail                    邮件收发类库
|     |- Message                    消息类库
|     |- Mongo                    Mongodb 类库
|     |- Page                    页面层(Controller)类库
|     |- Process                    多进程类库
|     |- Service                    服务层(Service)类库
|     |- Session
|     |- Socket                    Socket 类库
|     |- Util                    工具类库
|     |- View                    展示层(View)类库
|
|- hush-pms                    PHP Message Server

从上述目录结构说明中,我们可以看到Hush Framework的文件目录中,主要包含以下两大目录。

  1. hush-app目录
    该目录下的代码是Hush Framework给我们提供的框架实例程序,是一个比较完整的互联网应用实例,包括应用前端和管理后台两大部分,我们既可以把该实例当做一个代码示例库来学习和使用,也可以把它当做一个项目的基础架构进行二次开发;另外,之前也说过了,本书实例的服务端程序就是在本框架的基础之上开发的,因此从某种意义上来说和这里的实例程序是非常相似的,所以这里应该算是本书的重点之一了,接下来我们马上会对该实例中的一些主要用法和代码进行讲解。

特别注意一下hush-app下面的etc、lib、tpl和web四个目录,因为这四个目录分别是实例应用程序的配置目录、代码目录、模板目录和站点目录,下面我们来给大家详细介绍一下。
(1)配置目录(etc)
配置目录(etc)下面放置的都是应用的配置文件,其中比较重要的包括:全局配置文件(global.config.php)主要用于设置应用的总体配置,比如路径变量、类库位置等;数据库配置文件(database.mysql.php)用于设置数据库的服务器分布和分库分表策略等;前后台配置文件(frontend.config.php和backend.config.php)分别用于配置前后台的特殊参数。
(2)代码目录(lib)
本目录是主要的公用类库和逻辑代码目录,现将其中比较重要模块的代码分目录列举如下:权限控制模块(Acl目录)主要用于前后台的RBAC权限控制;控制器模块(App目录)用于各个页面的逻辑控制,也就是MVC中的Controller部分;工作流模块(Bpm目录)用于实例后台中工作流部分的逻辑控制;可执行程序模块(Cli目录)下面都是项目可执行程序的逻辑代码,另外我们需要知道的是hush-app/bin目录下面就是可执行程序的入口;数据操作模块(Dao目录)大家应该都非常熟悉了,这里保存的是和数据库操作相关的所有逻辑,也就是MVC中的Model部分。
(3)模板目录(tpl)
此目录下还分为前台模板目录(frontend目录)和后台模板目录(backend目录),分别用于存储应用实例前后台的Smarty模板,这也就是MVC中的View部分了,这里的模板和前面所提到过的“控制器模块”中的各个不同控制器的动作逻辑(Action)相对应。
(4)站点目录(web)
这里面放的都是一些静态文件或者独立的PHP代码等,此目录也分为前台站点目录(frontend目录)和后台站点目录(backend目录),另外这两个目录也是HTTP服务器的站点配置(DocumentRoot)所需要指定到的目录。

  1. hush-lib目录
    此目录保存的是Hush Framework的源代码,我们可以看到这里的代码目录和Zend Framework的结构非常一致,也就是把每个独立的模块代码都放在各自的目录下并尽量互不关联,这也比较符合“松耦合”的设计原则,既便于理解又便于阅读,是一个比较值得提倡的代码封装方法。

至此,已经给大家介绍了Hush Framework框架和应用(hush-app)中的重要代码目录,这是学习如何使用Hush Framework进行开发的重要一步,希望大家能好好消化一下以上内容。至于框架类库(hush-lib)源码中的模块和目录介绍,由于篇幅原因这里就暂时不做介绍了,有兴趣的读者可以访问Hush Framework在Google Code的官方站点,查找更多信息。

3.6.3 框架MVC思路讲解

首先,Hush Framework是一个标准的MVC框架,MVC的概念大家应该都耳熟能详了,由于关系到本书重要的服务端底层框架的学习,我们还是不得不老调重弹。MVC是模型(Model)、视图(View)和控制器(Controller)的缩写,是目前业内最主流且应用最广泛的软件设计模式之一,MVC具备以下主要优点。

  1. 低耦合性
    MVC最大的好处之一就是大大降低了程序的耦合性,特别是把视图层和业务逻辑分开之后,让整个应用灵活了很多;当业务逻辑有变化时,我们只需修改模型层和控制器的代码,而无须修改负责应用外观的视图层。
  2. 高重用性
    MVC的高重用性主要体现在两方面。一方面是视图层的重用性,因为在实际的项目中,我们经常需要修改应用外观来满足需求,由于业务逻辑已经分离出来,所以我们不需要更改逻辑就可以调整应用界面,满足了软件高重用性的要求。另一方面,业务逻辑被分为模型层和控制器层之后,既保证了核心数据结构的稳定性,也增强了业务逻辑控制器的灵活性,这样我们就可以很好地重用核心数据结构来实现多种多样的业务逻辑。
  3. 可维护性
    MVC设计模式把模型、视图和控制器分开,实际上也把设计者、程序员和UI设计人员的职能做了分离。这种分工方式不仅可以让应用的架构看起来更清晰,还便于软件维护时团队之间的共同协作,这对中大型软件应用程序的代码维护工作将起到很重要的作用。

之前讨论的是MVC设计模式中比较通用的概念和知识,然而对于不同的框架来说,具体的实现方式却有各自不同的特色,接下来我们就来学习Hush Framework中的MVC设计思路。为了让大家理解起来更容易,我们把Hush Framework处理客户端请求的完整过程通过图形的方式展示出来,如图3-16所示。
从图3-16中我们可以很清楚地看到客户端请求的整个处理过程,在一个标准的基于HTTP协议的互联网应用环境中,用户每次操作都会致使浏览器向HTTP服务器发送HTTP请求,当服务器接收到请求之后,就会调用框架程序来处理,此时Hush Framework就会接管接下来的工作。具体的处理流程一般分为以下几个步骤。
步骤1:首先,Hush Framework的请求分发器(Dispatcher)会分析客户端发送过来的HTTP请求所包含的信息,并根据请求的URL地址来指定使用相应的控制器(Controller)来处理该请求。
步骤2:接着,被指定的控制器会选择合适的模型类(Model)用于持久层数据的获取和存储,并负责处理该请求的业务逻辑。此外,我们可以看到Hush Framework的模型层是基于Zend Framework的模型层的。当然,在逻辑处理完成后,控制器还会调用视图层(View)来组合出最终的HTML代码。

《Android和PHP开发***实践 》一3.6 开发框架简介

步骤3:最后,服务器会把Hush Framework的处理结果通过HTTP协议返回给客户端程序来进行后续的处理。
通过分析我们会发现,实际上Hush Framework的MVC设计和实现的思路还是比较主流的,和大部分网络应用的MVC框架的思路也比较相似,不过其中还是有不少独特的亮点。比如Hush Framework的分发器(Dispatcher)就使用了独有的快速分发逻辑,大大提高了程序的运行效率;另外,不仅支持常见的URL路由分发模式,还支持通过“路由映射文件”这种更直观的方式来进行更精细的配置,映射文件代码如配置清单3-3所示。
配置清单3-3 etc/app.mapping.ini

; URL mappings
; 
; Used by Hush_App_Dispatcher class
; e.g /url/path = PageClassName::ActionName 
;

/                = DebugServer::indexAction
/debug/*            = DebugServer::*

另外,Hush Framework的模型层使用了Zend Framework作为底层框架,沿用了其完善的DB模型层封装和方法的设计,并使用自己独特的思路封装成Hush Framework的DAO基类,让建立在基类之上的持久层操作更加简便、高效。在接下来的3.6.4节中,我们将通过实例来讲解Hush Framework持久层的用法。

3.6.4 框架MVC实例分析

通过前面两节的学习,大家应该对Hush Framework框架的理论基础有了一定的认识,但是理论还是需要通过实践来证明,要真正学会如何运用该框架进行开发,光靠理论知识是远远不够的,所以在本节中我们将会围绕着框架实例中与MVC分层开发相关的实例代码为大家做进一步的讲解。下面我们会使用框架实例中的“后台登录”这个界面的完整逻辑,来给大家讲解一下在Hush Framework中我们是如何使用MVC的分层思路来进行编程的,我们先来看一下这个界面的截图,如图3-17所示。

《Android和PHP开发***实践 》一3.6 开发框架简介

以上是在浏览器中打开框架实例的后台站点时看到的界面,也就是后台的登录界面。这里我们可以看到浏览器中的地址是“http://hf-be/auth/”,按照前面所介绍的Hush Framework的应用目录中所提及的,对应的控制器类应位于lib/Ihush/App/Backend/Page/AuthPage.php文件中。至此,既然已经找到分析框架MVC用法的“突破口”,那么我们就从这里开始分析吧。
我们先来看看AuthPage.php文件中的AuthPage类,此类继承自Ihush_App_Backend_Page类(即整个实例应用的后台控制器基类),其中包含多个以Action为后缀的方法,分别对应于“后台登录”界面的几个逻辑,整个类的写法都是面向对象的,大家可以对照前面3.1.4节的内容理解一下,关于其中涉及的MVC分层思路,下面我们会把这三层的相关代码提取出来,分别给大家讲解一下。

  1. 控制器(Controller)
    控制器简单来说就是页面的逻辑,在Hush Framework中我们通常使用Page(页面)来表示通常意义的Controller,因为对于网络应用来说Page比Controller更好理解;此外,我们还需要知道Hush Framework使用的是REST格式的URL路径结构,自域名之后的URL路径第一个表示的是控制器的名称,第二个则是控制器的动作,也就是我们通常所称的Action,比如登录界面的路径为“/auth/”,其对应的逻辑就可以在AuthPage.php文件中AuthPage类的indexAction方法中找到,这里需要注意的是当前路径如果为空,我们则会用index来代替。代码清单3-21就是AuthPage类的完整实现,大家可以参考注释来阅读代码。

代码清单 3-21

/**
 * @package Ihush_App_Backend
 */
class AuthPage extends Ihush_App_Backend_Page
{
    public function indexAction () 
    {
        // TODO : 默认使用index.tpl作为Action的模板
    }
    
    public function loginAction () 
    {
        // 用户名/密码/验证码均不能为空
        if (!$this->param('username') ||
            !$this->param('password') ||
            !$this->param('securitycode')) {
            $this->addError('login.notempty');
        }
        // 验证码必须是正确的
        elseif (strcasecmp($this->param('securitycode'),$this->session('securitycode'))) {
            $this->addError('common.scodeerr');
        }
        
        // 通过参数验证
        if ($this->noError()) {
            // 使用DAO类的验证方法
            $aclUserDao = $this->dao->load('Core_User');
            $admin = $aclUserDao->authenticate($this->param('username'),
 $this->param('password'));
            // 登录失败(找不到用户)
            if (!$admin) {
                $this->addError('login.nouser');
            }
            // 登录失败
            elseif (is_int($admin)) {
                $this->addError('login.failed');
            } 
            // 登录成功
            else {
                // 是否是超级用户
                $admin['sa'] = strcasecmp($admin['name'], $this->sa) ? false : true;
                // 保存登录用户信息到会话
                $this->session('admin', $admin);
                // 跳转至首页
                $this->forward($this->root);
            }
        }
        
        // 登录失败则显示登录界面
        $this->render('auth/index.tpl');
    }
    
    public function logoutAction ()
    {
        if ($this->session('admin')) {
            // 用户登出,清除用户会话信息
            $this->session('admin', '');
        }
        
        $this->forward($this->root);
    }
}

从以上的代码中我们可以看出,在控制器类AuthPage中有三个Action方法,分别是indexAction、loginAction和logoutAction,这些方法对应的三个功能分别是“展示登录界面”、“用户登录逻辑”和“用户登出逻辑”,以上功能的基本逻辑和涉及的PHP语法这里就不做解释了,接下来我们会给大家分析一下这几个Action方法中比较重要的知识点,学习并理解这些知识点后,有助于我们分析AuthPage控制器类的代码。
(1)使用render方法展示模板
首先我们需要知道的是在Hush Framework中使用模板有两种方式:首先是默认方式,此种方式是按照“模板根目录/Controller名/Action名.tpl”这样的规则来放置的,因此对于indexAction来说我们可以根据这个规则分析出其对应模板是tpl/backend/template/auth目录下的index.tpl;另外一种方式是通过render手动设置模板,这也正是在loginAction中最后的那行代码所做的事情,可参考代码清单3-22中的写法。
代码清单 3-22

// 登录失败则显示登录界面
$this->render('auth/index.tpl');

这行代码的逻辑其实非常容易理解,就如同注释中描述的一样,当用户登录失败后,程序还是应该展示出登录页面给用户重新填写登录名和密码。
(2)使用param方法获取URL参数
param方法是开发中最常用的方法之一,该方法一般用于获得GET或者POST过来的URL参数;另外,如果这个函数带两个参数,则是设置对应URL参数的值。示例代码如代码清单3-23所示。
代码清单 3-23

// 获取GET或者POST过来的username参数值
$username = $this->param('username');
// 设置username参数值为james
$this->param('username', 'james');

(3)使用addError方法处理错误信息
大家都知道,在网页表单提交之后,我们会先做一些字段的判断,比如用户名和密码是否为空等,如果这些验证没有通过,就要给页面传递一些错误信息,而addError方法就是做这个事情的,比如前面实例中的代码“$this->addError('login.notempty');”就是用来显示错误信息的,另外对应的错误信息“login.notempty”我们可以在“etc/backend.errors.ini”文件中找到。
(4)使用load方法来加载DAO类
框架已经帮助我们把Controller层中如何使用Model层的方法封装好了,那就是这里所说的load方法,我们可以使用如下代码获取任意一个DAO类,如代码清单3-24所示。
代码清单 3-24

$aclUserDao = $this->dao->load('Core_User');
$admin = $aclUserDao->authenticate($this->param('username'), $this->param('password'));

以上代码取自于loginAction中的部分逻辑:首先,初始化了Core_User的DAO类供我们使用;然后,使用该类中的authenticate方法判断用户是否登录成功,这个地方的逻辑我们会在下面的模型层中做详细分析。
小贴士:前面提到的DAO是数据访问对象(Data Access Objects)的缩写,该对象常被用于进行数据库层面的各种数据操作,后面我们会经常提到。
(5)使用forward进行页面跳转
在一个互联网应用中,页面跳转是再常见不过的事情了,这里我们也能找到相应的示例代码,也就是登录成功之后跳转到首页的逻辑,如代码清单3-25所示。
代码清单 3-25

$this->session('admin', $admin);
$this->forward($this->root);

(6)使用session函数操控会话
会话的概念相信有些网络开发基础的朋友都应该清楚,因为HTTP是无状态的,所以我们一般会使用会话(Session)来保存用户相关的信息,在loginAction和logoutAction中我们都可以看到相关的代码,如代码清单3-26所示。
代码清单 3-26

// 保存登录用户信息到Session
$this->session('admin', $admin);
// 用户登出,清除用户会话信息
$this->session('admin', '');
  1. 视图层(View)
    视图层主要负责的是对应控制器逻辑的展示,一般来说是由HTML语法和Smarty变量构成的。根据前面介绍的关于Hush Framework中的两种模板使用方式,我们可以“顺藤摸瓜”找到indexAction对应的模板,也就是tpl/backend/template/auth中的index.tpl模板文件,如代码清单3-27所示。

代码清单 3-27

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>用户登录</title>
<link href="{$_root}css/main.css" rel="stylesheet" type="text/css" />
<link href="{$_root}css/login.css" rel="stylesheet" type="text/css" />
{literal}
<script type="text/javascript">
if(self!=top){top.location=self.location;}
</script>
{/literal}
</head>
<body>
<div class="login-body">
    <div class="login-con">
    <h1><img src="{$_root}img/logo_s.gif" /><span>后台管理系统</span></h1>
        <div class="login">
        {include file="frame/error.tpl"}
        <form action="{$_root}auth/login" method="post">
            <input type="hidden" name="go" value=""/>
            <input type="hidden" name="do" value="login"/>
            <ul>
                <li>
                    <label>用户名:</label>
                    <input type="text" class="text" name="username"/>
                </li>
                <li>
                    <label>密 码:</label>
                    <input type="password" class="text" name="password"/>
                </li>
                <li>
                    <label>验证码:</label>
                    <input type="text" class="text" style="width: 50px;margin-right:5px;
                    text-transform: uppercase;" id="securitycode"
                    name="securitycode" autocomplete="off"/>
                    <img id="securityimg" src="{$_root}app/scode/image.php"
                     alt="看不清?单击更换" align="absmiddle"
                 style="cursor:pointer" onClick="this.src=this.src+'?'" />
                </li>
                <li>
                    <input type="submit" onclick="this.form.submit();"
                    class="submit" value="登录" name="sm1"/>
                </li>
            </ul>
        </form>  
        </div>
    </div>
</div>
</body>
</html>

以上代码大部分是比较简单的HTML语法,穿插了一些Smarty的变量,比如“{$_root}”就是设置好的全局的Smarty的变量,代表项目URL的根路径,默认是“/”。另外,在该模板里我们还看到了登录表单的代码“

”,这里我们可以发现该登录表单将会被提交至“/auth/login”路径,其对应逻辑就在前面我们分析过的控制层中AuthPage类的loginAction方法里。
  1. 模型层(Model)
    模型层是MVC三层中最接近数据库的一层,里面放的是数据操作的逻辑,也就是说我们常说的CRUD操作,这部分也是我们需要重点了解的。在前面的登录界面的示例中,我们了解到loginAction中使用到了DAO类Core_User里面的authenticate方法,下面我们截取Core_User里面的相关代码给大家讲解一下,见代码清单3-28。

小贴士:CRUD操作是添加(Create)、查询(Retrieve)、更新(Update)和删除(Delete)的缩写,也就是我们常说的“增删查改”方法,这几个操作基本包含了数据操作类DAO绝大部分的使用方式,后面我们也会经常提到。
代码清单 3-28

/**
 * @package Ihush_Dao_Core
 */
class Core_User extends Ihush_Dao_Core
{
    /**
     * 设置表名
     * @static
     */
    const TABLE_NAME = 'user';
    
    /**
     * 设置主键
     * @static
     */
    const TABLE_PRIM = 'id';
    
    /**
     * Initialize
     */
    public function __init () 
    {
        $this->t1 = self::TABLE_NAME;
        $this->t2 = Core_Role::TABLE_NAME;
        $this->rsh = Core_UserRole::TABLE_NAME;
        
        // 绑定常用CRUD操作
        $this->_bindTable($this->t1);
    }
    
    /**
     * 登录验证方法
     * @uses Used by user login process
     * @param string $user 用户名
     * @param string $pass 密码
     * @return bool or array
     */
    public function authenticate ($user, $pass)
    {
        $sql = $this->select()
        ->from($this->t1, "*")
        ->where("name = ?", $user);
        
        $user = $this->dbr()->fetchRow($sql);
        
        if (!$user['id'] || !$user['pass']) return false;
        
        if (strcmp($user['pass'], Hush_Util::md5($pass))) return $user['id'];
        
        $sql = $this->select()
         ->from($this->t2, "*")
         ->join($this->rsh, "{$this->t2}.id = {$this->rsh}.role_id", null)
         ->where("{$this->rsh}.user_id = ?", $user['id']);
        
        $roles = $this->dbr()->fetchAll($sql);
        
        if (!sizeof($roles)) return false;
        
        foreach ($roles as $role) {
         $user['role'][] = $role['id'];
         $user['priv'][] = $role['alias'];
        }
        
        return $user;
    }
    ... 
}

接下来,我们来分析一下Core_User类中使用到的几个功能要点,并以此为实例给大家介绍一下Hush Framework中模型层的核心用法,也就是框架DAO基类中已经封装好的数据库常见操作的编码和使用。
(1)DAO类的初始化
在Hush Framework中使用DAO类,首先需要配置一个和数据表相对应的DAO类,这个过程我们通常称为DAO类的初始化。其实配置一个DAO类是非常方便的,因为框架DAO基类已经帮我们封装好了绝大部分DAO类所需要的逻辑和方法,所以初始化起来非常简单。代码清单3-29就是一个最简单的DAO类的范例模板。
代码清单 3-29

// DbName为数据库名
// TableName为数据表名
class DbName_TableName extends Dao_DbName {
    // 配置表名
    const TABLE_NAME = ' TableName ';
    // 配置主键名
    const TABLE_PRIM = 'PrimaryKey';
    // 初始化操作
    public function __init () {
         // 绑定常用的CRUD操作
         $this->_bindTable(TABLE_NAME);
    }
}

我们可以看到,区区几行代码就已经把一个DAO类写好了。以上代码中的DbName表示数据库名,TableName表示表名,PrimaryKey则表示主键名,__init是初始化方法,__bindTable主要用于绑定CRUD方法,也就是说,初始化之后我们就可以直接使用这个DAO类来进行“增删查改”操作了。
(2)DAO类中的查询方法
查询应该是数据库最主要的用途之一,这里我们会重点讲解在Hush Framework的DAO类中使用查询的要点。从前面提到的Core_User数据操作类中的authenticate方法中我们可以看到在DAO类中经常使用到的查询(select)方法的使用范例,包括普通查询和表关联查询,示例见代码清单3-30。
代码清单 3-30

… 
// 普通查询
$sql = $this->select()
    ->from($this->t1, "*")
    ->where("name = ?", $user);

$user = $this->dbr()->fetchRow($sql);
… 
// 表关联查询
$sql = $this->select()
    ->from($this->t2, "*")
    ->join($this->rsh, "{$this->t2}.id = {$this->rsh}.role_id", null)
    ->where("{$this->rsh}.user_id = ?", $user['id']);

$roles = $this->dbr()->fetchAll($sql);

在Hush Framework中,我们可以使用和Zend Framework类似的方式来“拼装”数据库SQL查询语句,其代码语法还是比较容易理解的,我们可以把其中的select方法、from方法、where方法以及join方法分别理解为SQL语句中的SELECT、FROM、WHERE以及JOIN这几个关键词,理解起来会更加清晰。当然除了以上这几个方法之外,框架底层还提供了LIMIT、GROUP BY和ORDER BY等常用SQL语句的对应方法。比如代码清单3-31中例举的就是一些相对复杂的SQL语句所对应的PHP代码的写法。
代码清单 3-31

// 对应标准SQL:
// SELECT COUNT(id) AS count_id
//     FROM foo
//     GROUP BY bar, baz
//     HAVING count_id > "1"

$select = $db->select()
    ->from('foo', 'COUNT(id) AS count_id')
    ->group('bar, baz')
    ->having('count_id > ?', 1);

// 对应标准SQL:
// SELECT * FROM round_table
//     ORDER BY noble_title DESC, first_name ASC

$select = $db->select();
    ->from('round_table', '*')
    ->order('noble_title DESC')
    ->order('first_name');

当然,我们需要理解Hush Framework的这种使用方法来替代SQL语句的做法,因为对于不同的数据库,查询语句区别是比较大的,如果没有一个很好的通用SQL语句的引擎很难做到良好的通用性,然而这却恰恰是本框架的优势所在;正是因为有底层的Zend_Db来提供强大的基础,才能让Hush Framework的模型层运转得更加得心应手。为了说明这点,我们以代码清单3-32为例,可以看到同样的DAO查询语句在不同的数据库中被解释成了不同的SQL;这样我们就不需要关心应用所使用的数据库类型,简便地写出通用型的代码,大大提高了模型层代码的重用性。
代码清单 3-32

// 在 MySQL/PostgreSQL/SQLite 中,对应 SQL 如下:
// SELECT * FROM foo
//     ORDER BY id ASC
//     LIMIT 10
//
// 在 Microsoft SQL 中,对应 SQL 如下:
// SELECT TOP 10 * FROM FOO
//     ORDER BY id ASC

$select = $db->select()
     ->from('foo', '*')
     ->order('id')
     ->limit(10);

此外,我们还需要注意一点,Hush Framework中的数据库类都是支持读写分离的,因此这里我们使用“dbr()->fetchRow(...)”方法(dbr是只读数据库db-read的缩写)来表示从“读库”中获取内容,一般来说数据查询操作中的绝大部分情况都会使用此方法;当然与之相对的,如果我们要写入数据的话,则应该使用dbw方法来操作“写库”,比如“dbw()->delete(...)”就是在“写库”中删除信息的写法。
(3)DAO类中的CRUD方法
前面我们已经介绍了DAO类中查询操作的用法,以及Hush Framework中对于数据库读写分离用法的使用要点,对于查询操作来说我们应该使用读库,但是对于CRUD中的其他几种操作来说就应该使用写库了,也就是使用“dbw()”方法进行调用,下面我们把除了select之外的几种方法给大家介绍一下。
create方法:此方法用于创建数据,只要传入的是包含数据的散列数组,我们就可以在数据表中添加一条记录。这里需要注意的是,我们在CRUD方法中传递的数据格式经常是类似“array(key1=>value1,key2=>value2...)”格式的数组,key是键名,对应的是数据表的字段名,而value则是数据,代表的是对应键名的数据。
exist方法:此方法用于来检测数据是否存在,一般来说我们可以传入主键值进行判断,当然如果我们需要根据其他字段的值来进行判断也是可以的,只需要在第二个参数传入对应字段名即可。
read方法:此方法也是和主键相关的,用于读取与对应主键相关的数据行。因为此方法不需要组装SQL,使用起来比select方法简单许多,所以在获取与主键有关的数据行的情况下我们常用它来替代select方法。如果不使用主键,我们也可以在第二个参数传入对应字段名。
update方法:此方法用于更新数据行,既可直接传入带主键的数组进行更新(此种情况将会按照主键值更新对应数据行)。当然,我们也可以在第二个参数传入where语句进行更新。
delete方法:此方法用于删除数据行,与前面的update方法类似,我们既可直接传入主键值进行删除(此种情况将会按照主键值删除对应行)。当然,我们也可以在第二个参数传入对应字段名。
replace方法:此方法用于替换数据行,在MySQL数据库中比较常用,一般我们替换的数据行也是和主键有关系的,或者是组合型主键。
到这里,我们已经把整个代码示例“登录界面”的逻辑介绍完了,同时也把Hush Framework中如何使用MVC的思路来进行编程的基本方法讲了一遍,现在大家应该对如何使用Hush Framework来进行开发心里有数了吧。由于Hush Framework是完全面向对象的,这里大家还可以学到许多PHP语言中的面向对象编程的技巧。当然***的学习方法就是动手,我建议大家把框架的实例代码架设起来,然后直接动手边调试边学习,以达到“学以致用”的***效果。另外,关于如何获取Hush Framework框架源码以及如何部署源码实例的内容,我们会在附录A中给大家做详细介绍。