模型 – 视图 – 控制器(MVC)体系结构的历史可以追溯到到Smalltalk编程语言和Xerox PARC。从那时起,已经有描述其架构MVC的许多系统。每个系统都略有不同,但都具有彼此分离的数据访问,业务逻辑和用户接口代码的结构。
大多数的PHP MVC框架都拥有以下执行过程
- 一个URL是由一个单一的PHP文件(通常称为前端控制器)截获。
- 这个PHP文件分析这个URL,然后得出一个控制器的名称和动作名称(这是通常被称为路由选择过程)。
- 派生控制器实例化。
- 与派生动作名一致的方法名叫做控制器
- 根据不同的请求变量,此操作方法实例化并调用模型上的方法
- 操作方法也将准备信息的数据结构。此数据结构传递给视图。
- 使用它从控制器接收到的所述数据结构中的信息将该视图呈现HTML
虽然这种模式是一个巨大的飞跃,从“每个PHP文件是一个网页”成立初期,一些软件工程师,它仍然是一个丑陋的黑客攻击模式。常见的抱怨是:
- 前端控制器的PHP文件仍然工作在全局命名空间。
- 约定优于配置会导致较少的模块化。
- 网址(URL)路由通常是不灵活的。
- 控制器通常绑定到特定的视图。
- 即使该系统拥有重写核心代码的功能,程序员依然只能在无尽的重构中编写代码。
正如你猜到的,Magento团队创建了一个更为抽象的MVC模式,大概的运行过程是:
- 一个URL是由一个单一的PHP文件拦截。
- 这个PHP文件实例化一个Magento的应用程序。
- Magento的应用程序实例化一个前端控制器对象。
- 前端控制器实例化任意数量的路由对象(在全局配置中指定)。
- 路由器检查请求的URL的“匹配”。
- 如果发现匹配,动作控制器和动作将被执行。
- 该控制器会被实例化,并且与动作同名的方法会被调用
- 被调用的方法根据请求的类型,对相应的模型调用相应的方法以获取数据
- 这个动作控制器将实例化一个布局对象。
- 根据请求中包含的变量及系统属性(通常叫做句柄),布局对象会为该请求创建一系列的Block对象
- 布局还会在相应的Block对象中调用输出方法,开始套嵌输出(Blocks之间的套嵌)
- 每个Block都有相应的模板文件。Blocks包含PHP的逻辑,模板包含HTML和PHP输出代码。
- Block从模型中获取相关数据,换句话说,控制器并不用来传递数据到视图中
我们最终会触及这一请求的每个部分,但现在我们关心的是前端控制器 – >路由器 – >动作控制器(Front Controller -> Routers -> Action Controller)部分。
Hello World
理论已经足够了,是时候Hello World了,我们将
- 在Magento系统中建立一个Hello World模块
- 配置模块的路由规则
- 为我们的路由创建控制器
创建Hello World 模块
首先,我们将创建这个模块的目录结构。我们的目录结构应该如下所示:
然后为这个模块创建配置文件 (路径 app/code/local/Magentotutorial/Helloworld/etc/config.xml):
在创建激活这个模块的文件 (at path app/etc/modules/Magentotutorial_Helloworld.xml):
最后,我们要确保这个模块激活状态:
- 清空Magento缓存
- 进入到Magento后台 System->Configuration->Advanced.
- 展开“Disable Modules Output”(如果还没有展开的话话)。
- 确保Magentotutorial_Helloworld显示出来。
配置路由
接着,我们开始配置路由规则,路由会将请求的URL地址分发到一个控制器和它的方法上。不像其它约定型的PHP MVC框架,在Magento中,你需要明确的在全局配置文件中配置路由规则,来告诉URL地址如何匹配对应的控制器和方法。
在你的config.xml文件,添加以下内容:
We have a lot of new terminology here, let’s break it down.
<frontend>是什么?
该标签涉及到Magento的一个术语Area。可以将Areas视为一些独立的Magento应用。“frontend” Area是Magento购物车应用的前端表现。”admin” Area是后端管理员应用。”install” Area是用来安装Magento的应用。
为什么配置一个模块的路由要使用复数<routers>呢?
引用一句Phil Karlton关于计算机科学的著名论断:
“There are only two hard things in Computer Science: cache invalidation and naming things”
意思是说计算机科学中最棘手的两件事就是缓存验证和命名。和许多大型系统一样,Magento同样受到命名的困扰。在全局配置文件书中,可以看到许多这种 甚至是丑陋的命名方式。<routers>便是其中之一。该标签通常会包括关于路由规则的配置信息,有时候又会包含实际的路由对象的配置信 息。这种命名方式初看起来有些不爽,但是随着你对于Magento系统的深入学习,你会逐渐改变对它的看法。
<frontName>是什么?
当路由器解析一个URL,它就会被分离出来,如下所示
通过在<frontName>标签中定义”helloworld”值,Magento就能够响应所有以下列URL访问的地址
许多开发Magento的新手容易混淆frontName与前端控制器对象。他们是不一样的东西。该frontName只属于路由。
<helloworld> tag作用是什么?
这个标签应该是你的模块名称的小写版本。我们的模块名称是的Helloworld,这个标签是helloword。从技术上讲这个标签定义了我们的路线名称
您还会注意到我们frontName符合我们的模块名称。这是有frontNames匹配模块名称宽松惯例,但它不是必需的。在你自己的模块,它可能是最好使用一个路由名称这是你的模块名称和包名的组合,以避免可能的命名空间冲突。
<module>Magentotutorial_Helloworld</module>的作用是什么?
该模块的标签应该是你的模块的全名,包括包/命名空间名称。这是利用该系统来定位控制器的文件。
为我们的路由创建动作控制器
最后一步,我们需要有自己的动作控制器。创建一个文件
包含以下内容
清除缓存配置,并加载以下网址
你也这样加载
您应该看到文本“Hello World”的一个空白页。恭喜你,你已经设置你的第一个Magento的控制器
动作控制器文件路径
控制器应该放在模块的controllers文件夹中,系统会自动在这个路径中寻找控制器。
控制器如何命名?
还记得在config.xml配置文件中的<module>标签吗?
控制器的命名应该:
- 以config.xml中指定该字符串开始(Magentotutorial_Helloworld)
- 后跟一个下划线 (Magentotutorial_Helloworld_)
- 这之后,就是动作控制器的名称 (Magentotutorial_Helloworld_Index)
- 最后,就是“控制器”的全名(Magentotutorial_Helloworld_IndexController)
所有的动作控制器需要继承Mage_Core_Controller_Front_Action。
没必要的”index/index”是什么?
正如我们前面所提到的,Magento的网址路由(默认情况下),如下所示
所以在URL中
URI中的”helloworld”是frontName,后面两个index分别是调用的控制器及方法名,即调用helloworld模块中的IndexController控制器中的indexAction方法。
如果URL是不完整的,Magento的采用“index”作为默认的,这就是为什么下面的网址是等价的。
如果我们有看起来像这样的网址
Magento 将做一下运行
- 在全局配置文件中找到使用frontName为checkout的模块(Mage_Checkout)
- 继续查询cart控制器 (Mage_Checkout_CartController)
- 调用cart控制器下的addAction()方法
其他动作控制器技巧
让我们尝试添加非默认的方法到我们的动作控制器。下面的代码添加到IndexController.php
然后访问URL来测试一下:
IndexController继承自Mage_Core_Controller_Front_Action类,有很多方法可以直接使用。例如,除上述URI中提到的三部分之外,其它部分会自动传给一个键值对数组。添加如下代码到IndexController中。
请访问以下网址
你应该可以看到每个参数和值打印出来。
最后,对于下面这个URL地址,系统应该如何响应呢?
该URL中,控制器的名字是messages,所以我们需要创建一个MessagesController控制器,在以下路径创建该文件
用动作控制器名为Magentotutorial_Helloworld_MessagesController和行动方法,看上去有点像
到此,概括地说,就是Magento的是如何实现MVC的控制器部分。虽然这是一个有点比其他PHP MVC框架的更为复杂,这是一个高度灵活的系统,可以让你构建几乎任何你想要的URL结构。