MVC结构可以追溯到Smalltalk编程语言和Xerox Parc时代,从那时起很多系统都自称采用了MVC结构。这些系统又都与其它有着些许差别,不过总体而言都会将获取数据、业务逻辑和用户界面的代码进行分离。PHP中常见的MVC框架如下图所示:
- 一个称为前台控制器的PHP文件获取到URL
- 该PHP文件检测URL并获取控制器名和Action名(这一步常被称为routing)
- 实例化所获取到的控制器
- 与获取的Action名相匹配的方法将会被调用
- 这个Action方法会根据请求的变量实例化/调用model中的方法
- 该Action方法准备好数据结构然后传送给view
- 视图根据从控制器那里获取的数据结构生成HTML
这种模式显然比早期的一个页面对应一个PHP文件要先进了很多,但软件工程师们在工作中会经常抱怨:
- 前台控制器在全局命名空间中工作
- 配置的规则只能实现少量的模块化
- URL routing不太灵活
- 即便系统允许重载默认方法,如果不进行大规模重构,这种模式会很难添加新的模型、视图或控制器。
基于此,Magento团队创建了更为抽象的MVC模式
- 由一个PHP文件获取到URL
- 该PHP文件实例化一个Magento应用
- Magento应用实例化一个前台控制器对象
- 前台控制器实例化一到多个Router对象(通过全局配置文件指定)
- Router检测请求的URL是否匹配
- 如果发现到匹配,会获取到Action控制器和Action
- 实例化Action控制器并调用与Action名称相匹配的方法
- Action方法根据请求实例化模型及调用方法
- 该Action控制器实例化Layout对象
- Layout对象根据请求对象和系统属性(也称作指针)创建一系列Block对象
- Layout还会在某些Block对象(启动内层渲染)上调用输出方法
- 每个Block都有一个对应的模板文件,Block中包含PHP逻辑,模板中包含HTML和PHP输出代码
- Block从模型中调取数据,也就是说Action控制器并不会传输数据结构。
我们会逐步揭开层层面纱,不过本节我们主要讨论前台控制器 > Router > Action控制器几部分。
这么多理论,想必大家多年的失眠症都已经治愈了…(⊙_⊙;)… 还是老规矩,下面用实例来进行说明,主要有以下几步:
- 创建一个Hello World模块
- 使用Router配置该模块
- 为Router创建Action控制器
首先为Helloworld模块创建目录:
app/code/local/Alanhou/Helloworld/Block app/code/local/Alanhou/Helloworld/controllers app/code/local/Alanhou/Helloworld/etc app/code/local/Alanhou/Helloworld/Helper app/code/local/Alanhou/Helloworld/Model app/code/local/Alanhou/Helloworld/sql
到这里大家估计已经是轻车熟路了,先创建app/code/local/Alanhou/Helloworld/etc/config.xml并加入如下代码:
<config> <modules> <Alanhou_Helloworld> <version>0.1.0</version> </Alanhou_Helloworld> </modules> </config>
紧接着创建app/etc/modules/Alanhou_Helloworld.xml文件来激活这个模块,加入如下代码:
<config> <modules> <Alanhou_Helloworld> <active>true</active> <codePool>local</codePool> </Alanhou_Helloworld> </modules> </config>
使用老方法确认一下模块是否配置成功
过上面基本的框架已经搭建好了,执行以下步骤
- 清除缓存
- 在后台中,点击System > Configuration > Advanced > Advanced
- 在Disable modules output面板中查看Alanhou_Helloworld是否出现
完成了模块的创建,下面我们就要进行Router的配置了,Router会将URL解释为Action控制器和方法,和传统PHP中的MVC系统不同,在Magento中需求明确地在全部配置中定义Router,打开config.xml,添加一段代码,修改为
<config> <modules> <Alanhou_Helloworld> <version>0.1.0</version> </Alanhou_Helloworld> </modules> <frontend> <routers> <helloworld> <use>standard</use> <args> <module>Alanhou_Helloworld</module> <frontName>helloworld</frontName> </args> </helloworld> </routers> </frontend> </config>
标签起来越越多的,让我们来逐一介绍
<frontend>是做什么的?
<frontend>代表Magento中的区域,可以把区域看作一个Magento应用,frontend区域就是面向客户的前台,也称为shopping cart应用。admin区域是管理后台应用而install区域是在第一次安装Magento时用到的应用。
不论是一个Router还是多用到的标签都是复数的<routers>
<frontName>又是做什么的?
在Router解析URL时,会进行如下的分离
http://example.com/frontName/actionControllerName/actionMethod/
所以通过将<frontName>定义为helloworld,我们实际上是在告诉Magento系统对应的URL是http://example.com/helloworld/*
很多开发者在刚开始的时候容易把frontName和前台控制器对象搞混,实际上frontName只和routing有关
<helloworld>标签有什么作用?
这个标签是模块名称的小写形式,这个模块名称是Helloworld,所以标签名就是helloworld,这实际上定义的是Router名称。
你可能发现frontName和模块名也是相同的,这是一种习惯,但并非强制要求。在自己写的模块中,拼接模块名和命名空间也许更好,这样可以避免一些不必要的名称重复。
<module>Alanhou_Helloworld</module>有什么作用?
这个module标签需要是模块的全称,包含命名空间,系统通过这个标签来定义控制器文件
接下来我们就进入最后一步,为我们的Router创建Action控制器。首先创建一个文件app/code/local/Alanhou/Helloworld/controllers/IndexController.php并添加如下代码:
<php class Alanhou_Helloworld_IndexController extends Mage_Core_Controller_Front_Action{ public function indexAction(){ echo 'Hello World!'; } }
清除缓存,然后在浏览器中访问http://localhost/magento/helloworld/index/index页面,就会发现屏幕上输出了Hello World!
Action控制器放在模块内controllers的文件夹内,其名称首先取自config.xml,本例中config.xml中有一段
1 |
<module>Alanhou_Helloworld</module> |
后面紧接着Action控制器的名称(Alanhou_Helloworld_Index),再在最后添加Controller,这样就构成了Alanhou_Helloworld_IndexController。所有的Action控制器都继承Mage_Core_Controller_Front_Action。
那么URL中的index/index又代表什么意思呢?前面我们提到过Magento的URL是按照下面这样解析的
http://example.com/frontName/actionControllerName/actionMethod/
http://example.com/frontName/actionControllerName/actionMethod/
假设我们访问http://example.com/checkout/cart/add,Magento会去全局配置文件中查找Mage_Checkout模块,名称为Mage_Checkout_CartController的Action控制器以及该控制器中的addAction方法
下面使用非默认的名称来创建一个方法,在IndexController.php中添加如下方法:
public function goodbyeAction(){ echo 'Goodbye World!'; }
这时访问http://localhost/magento/helloworld/index/goodbye就会输出Goodbye World!
由于这里我们继承了Mage_Core_Controller_Front_Action类,就会自然地获取一些该类中的方法,比如URL中多余的元素会被自动解析为键值对,在Action控制器中添加如下方法:
public function paramsAction(){ echo ' <dl>'; foreach($this->getRequest()->getParams() as $key=>$value){ echo ' <dt><strong>Param: </strong>'.$key.'</dt> '; echo ' <dt><strong>Value: </strong>'.$value.'</dt> '; } echo '</dl> '; }
然后访问http://localhost/magento/helloworld/index/params?foo=bar&baz=eof就会输出如下结果
最后我们再来创建一个响应http://localhost/magento/helloworld/messages/goodbye的文件,所以我们需要创建一个app/code/local/Alanhou/Helloworld/controllers/MessagesController.php文件。在其出添加一个名为Alanhou_Helloworld_MessagesController的Action控制器并添加一个goodbyeAction方法,具体代码如下:
<?php class Alanhou_Helloworld_MessagesController extends Mage_Core_Controller_Front_Action{ public function goodbyeAction(){ echo 'Another Goodbye!'; } }
访问http://localhost/magento/helloworld/messages/goodbye就会输出Another Goodbye!
以上就是如何在Magento添加MVC中的Controller,虽然比常见的MVC框架要复杂一些,但是具有超强的灵活性。