Note on <Zend Framework - A Beginner's Guide> - 02 ZF原理;继续解决第二章的问题

来源:互联网 发布:艾迪芬奇的记忆 知乎 编辑:程序博客网 时间:2024/05/31 18:52

没想到与上篇这个话题的文章已经隔了一个月零五天!!

在此书的第二章里面其实有一部分介绍ZF框架中的寻路(routing)概念也是比较重要的,现在增补:


MVC機制:


首先讲下ZF框架里,MVC三个元素和其他一些重要元素之间的关系:

interaction-between-mvc


Front Controller并不是我们这样的开发者需要接触到的,我觉得它指的是application/Bootstrap.php文件,或者说它的入口。


下面是一个请求在ZF框架下执行的流程:

1. 请求连接本身会被重写,以符合标准格式,这个重写是基于.htaccess文件完成的,重写后的请求被传递给index.php,就是网站入口。这个文件执行一些类似初始化的操作,然后就会建立一个front controller的实例,之后将控制交给它,我们来看下index.php的代码:

<?php// Define path to application directorydefined('APPLICATION_PATH')    || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));// Define application environmentdefined('APPLICATION_ENV')    || define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'production'));// Ensure library/ is on include_pathset_include_path(implode(PATH_SEPARATOR, array(    realpath(APPLICATION_PATH . '/../library'),    get_include_path(),)));/** Zend_Application */require_once 'Zend/Application.php';// Create application, bootstrap, and run$application = new Zend_Application(    APPLICATION_ENV,    APPLICATION_PATH . '/configs/application.ini');$application->bootstrap()            ->run();


就像刚刚我说的,我觉得书中所谓的front controller指的就是Bootstrap类,果不其然。


2. Front Controller检视请求,然后寻找相应的controller以及action。这个过程是使用模式匹配来完成的,所遵循的寻路规则除了包括默认的寻路机制外,还有开发者自定的(上一篇文章失败的那个实验)。


3. 一旦正确的controller被找到,控制就会被交给它,然后相应的action被执行,然后可能会有model被调用,和一些view被调用,最终输出结果给用户。


4. 如果没有任何controller匹配请求,将会有一个异常被抛出,而Error Controller会获取控制,它主要做的事情是基于发生的事情和一些预设定来输出一个错误页面到最终用户。来看看文件:application/modules/default/controllers/ErrorController.php:

<?phpclass ErrorController extends Zend_Controller_Action{    public function errorAction()    {        $errors = $this->_getParam('error_handler');                if (!$errors || !$errors instanceof ArrayObject) {            $this->view->message = 'You have reached the error page';            return;        }                switch ($errors->type) {            case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ROUTE:            case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER:            case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION:                // 404 error -- controller or action not found                $this->getResponse()->setHttpResponseCode(404);                $priority = Zend_Log::NOTICE;                $this->view->message = 'Page not found';                break;            default:                // application error                $this->getResponse()->setHttpResponseCode(500);                $priority = Zend_Log::CRIT;                $this->view->message = 'Application error';                break;        }                // Log exception, if logger available        if ($log = $this->getLog()) {            $log->log($this->view->message, $priority, $errors->exception);            $log->log('Request Parameters', $priority, $errors->request->getParams());        }                // conditionally display exceptions        if ($this->getInvokeArg('displayExceptions') == true) {            $this->view->exception = $errors->exception;        }                $this->view->request   = $errors->request;    }    public function getLog()    {        $bootstrap = $this->getInvokeArg('bootstrap');        if (!$bootstrap->hasResource('Log')) {            return false;        }        $log = $bootstrap->getResource('Log');        return $log;    }}


寻路规则:


URL的格式总是遵循:/module/controller/action。我们用归纳法来解释:


http://application/auto/listing/save

上面的这个请求将会导向auto模块的ListingController::saveAction动作。

http://application/content/news/edit

上面的这个请求将会导向content模块的NewsController::editAction动作。


如果URL中某些參數是缺失的,ZF將根據下列規則尋路:


如果module是缺省的,default模塊會被使用;

如果controller是缺省的,IndexController會被使用;

如果action是缺省的,indexAction會被使用。


所以:

http://application/default/contact/send等於http://application/contact/send。

http://application/default/post/index等於http://application/post/。

http://application/content/news/index等於http://application/content/news。


同理,請求http://localhost/square/public等於http://localhost/square/public/default/index/index

它導向default模塊的IndexController的indexAction。而IndexController.php是之前通過ZF工具自動創建的,其內容為:

<?phpclass IndexController extends Zend_Controller_Action{    public function init()    {        /* Initialize action controller here */    }    public function indexAction()    {        // action body    }}


從這個文件中看不到任何實際的操作,所以可見,ZF自身包含一套機制去將控制從Controller轉向相應的view類。


命名法則:


先說下軟件界內著名的“camel-casing”,駱駝規則的意思是:當多個英文單詞連在一起時,將每個單詞的首字母大寫。


ZF框架中controller和action的命名規則是基於駱駝規則,Controller的名字是嚴格的駱駝規則,而action的名字則是除了駱駝規則外,第一個單詞的首字母小寫。


但是,上述情况是针对default模块(module)的,如果是其他模块下,controller的名字前需要加个前缀,前缀的格式是:<模块名>_。比如News_IndexController。


在前一篇文章里,我已经按照此书建议,将application/下的默认模块的文件都移动到application/modules/default/下面,在这样的文件结构里,所有的PHP文件都会以模块来划分,并放在modules/下的不同文件夹里,文件夹的名字即是模块的名字。而在模块文件夹内,有controllers,models,和views三个文件夹,上面介绍的controller的PHP文件就放在controllers/下。


至于视图文件,它们都是放在views/scripts/路径下,但是,按照相对应的controller来组织,比如目前,在default模块下已经有三个controller,分别是:

default-controllers

而与之对应的views/script/的情况是:

default-views

视图文件夹的命名规则是:使用其相对应的controller的名字(当然要出去后面的“Controller”后缀),全部字母小写,如果超过一个以上单词,那么单词之间可以用“-”分隔开来,当然你不想也行。view文件的命名是:使用其对应的action的名字(当然要出去后面的“Action”后缀),全部字母小写,如果超过一个以上单词,那么单词之间可以用“-”分隔开来。比如在static-content/路径下,有两个视图,分别对应着两个动作:

default-static-content-view


回到自定路径的问题


上次的文章里按照书中指示,做了两个自定义路径,第一个是用/home指向/default/index/index。这个成功了。但是第二个用/content/:page指向新创建的default/staticContent/display,但是这个实验失败了。


当然,书中并未详细解释ZF框架在控制从conteoller与view之间传递过程中,所有自动呼叫某些方法的机制。


总之,按照上篇文章所讲的,为了调试,我在StaticContentController.php中加了几行代码用来输出变量到外部文件里:

$fp = fopen('console.log', 'a+');fwrite($fp, $page."\n");fclose($fp);


而实际上这个文件被创建在(参见PHP的fopen方法)/public/下面,即是与index.php同一目录,所以可见,所有请求的整个运行环境都是以index.php为上下文的。



至此为止,我们已经回到上次的断点了,可是笔者遇见一些暂时无法解释的事。当我现在按照书中指示,再次在浏览器中访问链接:http://localhost/square/public/content/about-us或者http://localhost/square/public/content/services时,我见到了正确的结果。而在上一次实验中,我是失败的。

works-well-static-content


我个人揣测,或许是这种程度的修改需要重新启动Apache才能生效,所以上次是失败的。


延伸探究


因为按照书中解释,跟在URL最后面的字符串,比如上面的services,将会被重写为之后在controller中可以通过page变量名访问的参数,但是究竟具体为何,并未解释,是否即是将其重写成URL?page=services的格式呢


所以我尝试在浏览器访问:http://localhost/square/public/default/staticcontent/display?page=services,得到的是个Page Not Found的错误:

page-not-found

但是,同时,在我的调试输出的文件里,我见到输出:services,即是说,将变量写成URL参数的形式的话,在controller里还是能够通过$this->getRequest()->getParam('page')获得这个参数的值,但是至于为何之后出现错误,就暂时不知。


所以我在displayAction()中输出整个if判断中所寻找的视图文件的地址:$this->view->getScriptPath(null) . "/" . $this->getRequest()->getControllerName() . "/$page." . $this->viewSuffix

得到的结果为:D:/idsweb/htdocs/square/application/modules/default/views\scripts//staticcontent/services.phtml

而当能够正常访问时,得到的是:D:/idsweb/htdocs/square/application/modules/default/views\scripts//static-content/services.phtml;


所以可见,getControllerName()得到的结果不一样,因为按照我们在application.ini里所添加的代码来看,我们指定这个controller时所使用的名称是static-content,而不是staticcontent。所以尝试访问:http://localhost/square/public/default/static-content/display?page=services:

得到正确的输出。