
2.3 了解Zend Framework的MVC结构
Zend Framework框架是一个良好的MVC架构的框架,一个页面一般由Mode(l M)、View(V)和Controller(C)三部分来共同生成,接下来让我们先通过修改首页来简单了解一下MVC的结构。上一节我们安装好了Zend Framework,通过浏览器看到了一个首页,那么这个页面是怎么来的?如何去改变它呢?打开application/views/scripts/index文件夹里的index.phtml,可以看到这就是首页的HTML代码,它应该是如下内容:
application/views/script/index/index.phtml <style> a:link, a:visited { color: #0398CA; } span#zf-name { color: #91BE3F; } div#welcome { color: #FFFFFF; background-image: url(http://framework.zend.com/images/bkg_header.jpg); width: 600px; height: 400px; border: 2px solid #444444; overflow: hidden; text-align: center; } div#more-information { background-image: url(http://framework.zend.com/images/bkg_body-bottom.gif); height: 100%; } </style> <div id="welcome"> <h1>Welcome to the <span id="zf-name">Zend Framework!</span></h1> <h3>This is your project's main page</h3> <div id="more-information"> <p><img src="http://framework.zend.com/images/PoweredBy_ZF_4LightBG.png" /></p> <p> Helpful Links: <br /> <a href="http://framework.zend.com/">Zend Framework Website</a> |<a href="http://framework.zend.com/manual/en/">Zend Framework Manual</a> </p> </div> </div>
我们将该文件中的全部代码删除,重新写一行:
Hello, World!
刷新页面,会看到首页已发生改变,如图2-10所示。

图2-10 修改视图后的首页
对于初次接触框架的学习者来说,这显得有点匪夷所思:明明访问的是根目录,为什么打开的却是如此奇怪的路径下的一个奇怪文件呢(注意,扩展名不是php也不是html,而是phtml,这是可以更改的)?这个问题可能有很多种回答方式,但我觉得最简单直接的回答是——没有为什么,就是一个约定,为了让程序的逻辑代码与表现代码分开,人们设定了这种机制以使其更好地工作。
现在打开application/controller/IndexController.php文件,可以看到其中的内容如下:
application/controller/IndexController.php <?php class IndexController extends Zend_Controller_Action { public function init() { /* Initialize action controller here */ } public function indexAction() { // action body } }
该文件中有init()和indexAction()两个方法,init()方法我们会在稍后介绍,而indexAction()方法会直接控制首页的内容。接下来,让我们在indexAction()方法中添加一个输出:
application/controller/IndexController.php public function indexAction() { // action body echo "人,诗意地栖居大地……"; } }
此时再在浏览器中刷新首页,可以看到控制器index方法中输出的字符串也出现在首页中,如图2-11所示。

图2-11 修改Index控制器的index方法的首页
刷新之后,“人,诗意地栖居大地……”这一荷尔德林的名句就由你撰写的控制器,清晰地输出到了浏览器之中。也就是说首页的内容既可以由视图文件来输出,也可以由控制器来输出,而且显然,视图中的内容输出较为优先。
现在,试着访问一下这个地址:http://kehuan.edu/index/index,看到的还是同样的首页对吗?http://kehuan.edu/省去了后边的index/index,它们实际访问的是同一个页面。我们先来试着看看页面的URL、控制器和视图三者之间是什么关系。
当 我 们 访 问 首 页http://kehuan.edu/index/index时,框 架 会 帮 你 去 找application/controllers/IndexController.php文 件 里 的indexAction(),同 时 也 会 去 找application/views/scripts/index/index.phtml,输出这两部分内容,indexAction()方法和index.phtml视图文件两者缺一不可。同样道理,假设我们要访问http://kehuan.edu/blog/create,就需要有一个在application/controllers文件夹下的名为BlogController.php的文件,其中应该创建一个名为createAction()的方法,同时还需要在application/views/scripts/blog文件夹下创建一个名为create.phtml的视图文件。对于Zend Framework框架的MVC规则,这是些简单但重要的认识,接下来我们将分别对控制器(C)、视图(V)和模型(M)做更进一步的介绍。
如果此时中文显示为乱码,是因为我们还没有为HTML页面指定编码,请暂时先在浏览器中将字符编码设为utf-8,即可正常显示。我们在下一章的layout布局模板中会为全站页面指定编码。
2.3.1 Controller控制器
控制器默认在application\controllers目录下,它可以被理解为一个管理者。它首先接收URL,根据URL分析判断用户的请求,然后调用系统中的相关资源处理请求,给出结果,最后还负责把结果传输给视图。在MVC中,控制器扮演着最为重要的角色,它是不可缺少的。
控制器的命名必须遵循固定的规范。Zend Framework从PEAR中借鉴了一个思想,即类名和文件系统相互对应,通过文件名即可知道其路径,反之亦然,其方法是简单地将下画线“_”替换为目录分隔符,然后加上后缀“.php”。例如,“Foo_Bar_Baz”将对应文件系统中的“Foo/Bar/Baz.php”。
注意,通常控制器与控制器文件是两个概念,控制器是一个类,而控制器文件是指存放这个类的那个PHP程序文件。
每个控制器中都有若干个方法,一般来说,一个方法对应一个页面。
在Zend Framework中,如果访问一个不存在的页面,如http://kehuan.edu/hotdog,将会抛出错误提示,如图2-12所示。

图2-12 不存在页面错误
这个错页面页是由application/controllers/ErrorController.php控制器输出到视图文件
application/views/script/error/error.phtml生成的。
2.3.2 Model模型
如果把控制器比做一个管理者的话,模型则可视做一个技术人员,它负责具体的事务,为管理者提供所需要的东西,它跟数据库的关系最为密切,总是干一些从数据库里查找、存进和取出的工作。
当我们使用ZF tool时,会注意到该工具会更新根目录下一个名为.zfproject.xml的文件。
我们打开.zfproject.xml,找到以下这一行代码:
<applicationDirectory classNamePrefix="Application_">
将其中的Application修改为Kh:
<applicationDirectory classNamePrefix="Kh_">
这样,当我们创建Model时,模型的前缀将会是Kh_。
我们也可以通过以下命令来修改:
zf change application.class-name-prefix Kh_
你可以试着在终端中执行以下命令来创建一个名为name的模型:
zf create model name
执行完之后,你会发现在application/models文件夹下,多了一个Name.php文件,打开后其内容如下所示:
<?php class Kh_Model_Name { }
这是一个崭新的模型,等待着你在需要的时候编写它的内容。
2.3.3 View视图
视图是MVC中最好理解的部分,它属于前台,也就是要交给网页美工去搞定的东西,它的大部分代码都是HTML。我们可以把它比做一个艺术家,它接收控制器给的东西,以它们为原材料,进行艺术加工,使其成为最终用户看到的东西——布局合理、色彩悦目的网页,让人看了之后印象深刻。
视图文件默认的路径是application\views\scripts目录,每一个控制器对应一个文件夹,该控制器中的每个方法,对应该文件夹下的某个文件,控制器与文件夹、方法与文件的名称,都有相应的命名规范。举例来说,假如我们有一个名为ChinaController的控制器,该控制器中有peopleAction()、cityAction()、cultureEducationAction()三个方法,那么相应地在application/views/scripts目录下,应该有一个china文件夹,其中有people.phtml、city.phtml和culture-education.phtml三个文件。请注意以上控制器名、方法名和视图文件名的大小写:控制器是每个单词的首字母都大写(即大驼峰式写法),方法则是第一个单词首字母小写,后面每个单词的首字母都大写(即小驼峰式写法),所有的视图文件夹和文件的名称都是全部小写,每个单词之间用连接符“-”连接。
2.3.4 路由和分发规则
要彻底搞明白Zend Framework的工作机制和流程,实在不是一件容易的事情。学习了MVC之后,我们还需要对框架的路由和分发规则有一些了解。如果你手头有Zend Framework手册,你会找到一大段介绍工作流程的文字,里面有许许多多莫测高深的术语:前端控制器、路由、分发器、扫描、分发、请求和响应、动作助手……如果你是新手,建议还是别去读这些东西,Zend Framework官方手册的撰写者把这些东西整理出来大概是写给开发这个框架的同事们看的,而不是给用框架来开发的人阅读的,其中的术语和不知所云的陈述会把新手迅速搞晕,彻底失去学习的兴趣和信心。对刚开始学习的人来说,目前Zend Framework手册中比较有价值的一部分是Quick Start。
言归正传。在这一部分,我们带领大家了解一下Zend Framework的工作流程。请注意,只是简单地了解一下,而非深入探究。不结合编码开发实践而只通过文字,是很难把这个流程弄明白的,而且在接下来的实例中,这部分内容显得并不是很重要,它并不应该成为入门的障碍,你大可在具有一定基础后,再来深入研究它。
Zend Framework实现了Front Controller模式,这意味着,所有的HTTP请求都会被转发到同一个入口,然后再路由到相应的控制器。这个入口就是root根目录下的index.php文件。Zend Framework通过URL Rewrite技术来实现这一点。在root根目录下,存在一个.htaccess文件,内容如下:
RewriteEngine On RewriteCond %{REQUEST_FILENAME} -s [OR] RewriteCond %{REQUEST_FILENAME} -l [OR] RewriteCond %{REQUEST_FILENAME} -d RewriteRule ^.*$ - [NC,L] RewriteRule ^.*$ index.php [NC,L]
以上配置将根目录下没有的文件和路径请求都转发到index.php,然后通过index.php来路由。
一个请求被产生,一个相应的响应就被返回。上面这个流程发生在前端控制器(front controller)内部,这个过程常常是在前端控制器(front controller)调用dispatch()方法时触发的,这个过程可以分解为下列12个小步骤:
① 一个请求Request的产生(创建了一个Request Object对象)。
② 路由事件routeStartup触发。
③ 路由器Router开始处理这个请求,从中获取请求信息。
④ 路由事件routeShutdown被触发,路由过程结束。
⑤ 派遣事件dispatchLoopStartup被触发。
⑥ 派遣preDispatch事件被触发。
⑦ 派遣过程中调用动作控制器(action controller)。
⑧ 动作控制器(action controller)将处理完成信息直接写入响应对象(response object)。
⑨ 派遣postDispatch事件被触发。
⑩ 检测派遣标志,即检查是否还有动作没有完成,如果有再次进入派遣循环(第6步)。
⑪ 派遣事件dispatchLoopShutdown被触发。
⑫ 产生的响应Response被返回。
以上流程可以用一个流程图2-13来直观地表示。

图2-13 Zend Framework框架的前端控制器执行流程(路由过程)
看着是不是有点头大?这的确很复杂,好在我们在此并不试图把这个过程全部阐释清楚,所以看不懂也没关系,可以在后面的学习过程中结合具体的开发实践慢慢来理解。Zend Framework已经安装配置完毕,让我们继续上路,尽快开始写出一些东西来看看吧!