深入理解Magento – 第三章 – 布局,块和模板

深入理解Magento

作者:Alan Storm
翻译:Hailong Zhang

第三章 – 布局,块和模板

我们接着研究Magento。根据我们第二章讲的Magento MVC的架构,我们接下来应该讲模型(Model),但是我们跳过模型先来看布局和块。和一些流行的PHP MVC架构不同的是,Magento的执行控制器不直接将数据传给试图,相反的视图将直接引用模型,从模型取数据。这样的设计就导致了视图被拆分成两部 分,块(Block)和模板(Template)。块是PHP对象,而模板是原始PHP文件,混合了XHTML和PHP代码(也就是把PHP作为模板语言 来使用了)。每一个块都和一个唯一的模板文件绑定。在模板文件phtml中,“$this”就是指该模板文件对应的快对象。

让我们来看一个例子
File: app/design/frontend/base/default/template/catalog/product/list.phtml
你将看到如下代码
`getLoadedProductCollection() ?>
count()): ?>

__('There are no products matching the selection.') ?>

这里“getLoadedProductCollection”方法可以在这个模板的块对象“Mage_Catalog_Block_Product_List”中找到File: app/code/core/Mage/Catalog/Block/Product/List.php

public function getLoadedProductCollection()
{
return $this->_getProductCollection();
}
…`
块的“_getProductCollection”方法会实例化模型,并读取数据然后返回给模板。

嵌套块

Magento把视图分离成块和模板的真正强大之处在于“getChildHtml”方法。这个方法可以让你实现在块中嵌套块的功能。顶层的块调用第二层的块,然后是第三层……这就是Magento如何输出HTML的。让我们来看一下单列的顶层模板
File: app/design/frontend/base/default/template/page/1column.phtml

`

getChildHtml('head') ?> getBodyClass()?'':'' ?>> getChildHtml('after_body_start') ?>
getChildHtml('global_notices') ?>
getChildHtml('header') ?>
getChildHtml('breadcrumbs') ?>
getChildHtml('global_messages') ?> getChildHtml('content') ?>
getChildHtml('footer') ?> getChildHtml('before_body_end') ?>
getAbsoluteFooter() ?> ` 我们可以看到这个模板里面很多地调用了“$this->getChildHtml(…)”。每次调用都会引入另外一个块的HTML内容,直到最底层的块。

布局对象

看到这里,你可能有这样的疑问
Magento怎么知道在一个页面上要用那些块?
Magento怎么知道哪一个块是顶层块?
“$this->getChildHtml(…)”里面的参数是什么意思?块的名字吗?
Magento引入了布局对象(Layout Object)来解决上面的那些问题。布局对象(或者说布局文件)就是一个XML文件,定义了一个页面包含了哪些块,并且定义了哪个块是顶层块。

在第二章的时候我们在执行方法(Action Method)里面直接输出了HTML内容。现在我们要为我们的Hello World模块创建一个简单的HTML模板。首先我们要创建如下文件
app/design/frontend/default/default/layout/local.xml
包含以下内容
<layout version="0.1.0"> <default> <reference name="root"> <block type="page/html" name="root" output="toHtml" template="../../../../../code/local/Alanstormdotcom/Helloworld/simple_page.phtml" /> </reference> </default> </layout>
再创建如下文件
app/code/local/Alanstormdotcom/Helloworld/simple_page.phtml
包含以下内容
`

Untitled ` 最后,我们要在执行控制器里面调用布局文件,开始输出HTML。修改执行方法如下 `public function indexAction() { //remove our previous echo //echo 'Hello Index!'; $this->loadLayout(); $this->renderLayout(); }` 清空Magento缓存,访问URL “http://exmaple.com/helloworld/index/index”。你应该看到一个纯红色背景的页面。这个页面的源代码应该和我们创建的文件“simple_page.phtml”一模一样。

究竟是怎么回事呢?

也许你看到这里一头雾水,没关系,我们来慢慢解释。首先你得安装一个 Layout Viewer 模块,这和我们第一章讲的 Config Viewer 模块很相似,都是查看Magento的内部信息。安装完这个模块之后【译者注:你需要参照第一章的内容,为这个模块创建“app/etc/modules/Alanstormdotcom_Layoutviewer.xml”】,打开如下URL
http://example.com/helloworld/index/index?showLayout=page
你看到的是你正在请求的页面的布局文件。它是由组成的。当你在执行方法中调用“loadLayout”时,Magento会做如下处理
生成这个布局文件
为每一个标签实例化一个块对象。块对象的类名是通过标签的name属性来查找的。这些块对象被存储在布局对象的_blocks数组中
如果标签包含了output属性,那么这个块的名字和output属性的值会被添加到布局对象的_output数组中
然后,当你在执行方法中调用“renderLayout”方法是,Magento会遍历_output数组中所有的块名字,从_blocks数组中获得该 名字的块,并调用块对象中使用output属性的值作为名字的函数。这个函数往往是“toHtml”。这个output属性也告诉Magento这里就是 输出HTML的起点,也就是顶层块。【译者注:直接阅读Layout类的代码应该比较容易理解这里的逻辑
File: app/code/core/Mage/Core/Model/Layout.php public function getOutput() { $out = &#039;&#039;; if (!empty($this->_output)) { foreach ($this->_output as $callback) { $out .= $this->getBlock($callback[0])->$callback[1](); } } return $out; }
从这里我们也可以看出,一个页面的布局文件时可以拥有多个顶层块。

下面我们要讲解块对象是如何被实例化的,这个布局文件时如何被生成的,最后我们将动手做一个例子来实践这一章讲的内容。

实例化块对象

在布局文件中,标签有一个“type”属性,这个属性其实是一个URI
<block type="page/html" ... <block type="page/template_links"
Magento就是通过这个URI是用来查找块对应的类名。这个URI分为两部分,第一部分“page”是用来在全局配置中查找一个基本类名,第二部分“html”或者“template_link”将被添加到基本类名后面生成一个具体的将被实例化的类名。

我们以“page/html”为例。首先Magento在全局配置中找到节点
/global/blocks/page
有以下内容
<page> <class> Mage_Page_Block </class> </page>
这里我们拿到了一个基本类名“Mage_Page_Block”,然后添加URI的第二部分“html”到基本类名后面,我们就得到最终的块对象的类名 “Mage_Page_Block_Html”。块的类名在Magento中被称为“分组类名”(Grouped Class Names),这些类都用相似的方法被实例化。我们将在以后的章节中详细介绍这个概念。

的区别

我们上面提到都会实例化块对象,那么它们究竟有什么区别呢? 在布局文件中是用来表示替换一个已经存在的块,举个例子
`

` Magento首先创建了一个名叫“root”的块。然后,它有发现了一个引用(reference)的名字也叫“root”,Magento会把原来那个“root”块替换成标签里面的那个快。

再来看看我们之前创建那个local.xml
<layout version="0.1.0"> <default> <reference name="root"> <block type="page/html" name="root" output="toHtml" template="../../../../../code/local/Alanstormdotcom/Helloworld/simple_page.phtml" /> </reference> </default> </layout>
在这里,块“root”被我们用替换了,指向了一个不同的模板文件。

布局文件是如何生成的

现在我们对布局文件已经有所了解了,但是这个布局文件是那里来的呢?要回答这个问题,我们得引入Magento中的另外两个概念,操作(Handle)和包布局(Package Layout)。

操作

Magento会为每一个页面请求生成几个不同的操作。我们的Layout View模块可以显示这些处理器
http://example.com/helloworld/index/index?showLayout=handles
你应该看到类似如下列表的列表(和你的配置有关)
default STORE_bare_us THEME_frontend_default_default helloworld_index_index customer_logged_out
它们每一个都是一个操作的名字。我们可以在Magento系统的不同的地方配置操作。在这里我们需要关注两个操作 “default” 和 “helloworld_index_index”。“default”处理器是Magento的默认处理器,参与每一个请求的处理。 “helloworld_index_index”处理器的名字是frontname “helloworld”加上执行控制器的名字“index”再加上执行方法的名字“index”。这说明执行控制器的每一个执行方法都有一个相应的操 作。

我们说过“index”是Magento默认的执行控制器和执行方法的名字,所以以下请求的操作名字也是“helloworld_index_index”。
http://example.com/helloworld/?showLayout=handles

包布局

包布局和我们以前讲过的全局配置有些相似。它是一个巨大的XML文档包含了Magento所有的布局配置。我们可以通过以Layout View模块来查看包布局,请求一下URL
http://example.com/helloworld/index/index?showLayout=package
你可能要等一会儿才能看到输出,因为文件很大。如果你的浏览器在渲染XML的时候卡死了,建议你换成text格式的
http://example.com/helloworld/index/index?showLayout=package&showLayoutFormat=text
假设你选择的是XML格式输出,那么你应该看到一个巨大的XML文件,这就是包布局。这个文件时Magento动态生成的,合并当前主题(theme)下面所有的布局文件。如果你用的是默认安装的话,这些布局文件在以下目录
app/design/frontend/base/default/layout/
其实在全局配置中,有一个节点下面定义了所有将被装载的布局文件
<layout> <updates> <core> <file>core.xml</file> </core> <page> <file>page.xml</file> </page> ... </updates> </layout>
当这些文件被装载以后,Magento还会装载最后一个布局文件,local.xml,也就是我们之前新建的那个文件。我们可以通过这个文件来定制Magento的布局。

结合操作和包布局

在包布局文件中,我们可以看到一些熟悉的标签等等,但是他们都包含在一下这些标签中
<default /> <catalogsearch_advanced_index /> etc...
这些就是操作标签。对于每个特定的请求来说,针对这个请求的布局文件是由包布局中所有和这个请求相关的操作标签组成的。比如我们上面的例子,和请求相关的操作标签如下
<default /> <STORE_bare_us /> <THEME_frontend_default_default /> <helloworld_index_index /> <customer_logged_out />
所以,针对请求
http://example.com/helloworld/index/index
布局文件就是包布局中上面这些标签的内容组合。在包布局文件中,还有一个标签值得我们注意。我们可以通过这个标签引入另外一个操作标签。比如
`

` 这段代码的意思是,如果一个请求包含了“customer_acount_index”操作,那么这个请求的布局文件也应该包含“customer_account”操作标签下面的

更新我们的例子

好了,理论讲完了,让我们来修改我们的例子,把这一章的内容实践一下。我们重新来看local.xml
<layout version="0.1.0"> <default> <reference name="root"> <block type="page/html" name="root" output="toHtml" template="../../../../../code/local/Alanstormdotcom/Helloworld/simple_page.phtml" /> </reference> </default> </layout>
我们用一个引用(reference)覆盖了名为“root”的块。然后定义了一个新的块,指向了一个不同的模板文件。我们把这个引用放 在操作标签下面,那就说明这个Layout将对所有的请求有效。如果你访问Magento自带的一些页面,你会发现它们要门是空白,要么就是和我 们“hello world”例子的红色背景,但这并不是我们想要的效果。我们来修改一下local.xml,让我们的模板仅对“hello world”的请求有效。
<layout version="0.1.0"> <helloworld_index_index> <reference name="root"> <block type="page/html" name="root" output="toHtml" template="../../../../../code/local/Alanstormdotcom/Helloworld/simple_page.phtml" /> </reference> </helloworld_index_index> </layout>
我们把操作标签换成了“helloworld_index_index”。清空Magento缓存,重新访问Magento的各个页面,你应该发现都恢复了正常,但是针对”hello world”模块的请求页面还是我们自定义的那个。

目前我们只实现了一个“index”执行函数,现在我们来实现“goodbye”执行函数。修改我们的执行控制器代码如下
public function goodbyeAction() { $this->loadLayout(); $this->renderLayout(); }
但是你访问一下页面的时候你还是会看到Magento的默认布局
http://example.com/helloworld/index/goodbye
那是因为我们没有为这个请求定义布局。我们需要在local.xml中添加“helloworld_index_goodbye”标签。由于 “index”请求和“goodbye”请求我们要套用的布局是一样的,所以我们将用标签来重用已有的配置
`

` 清空Magento缓存,请求以下URL `http://example.com/helloworld/index/index http://example.com/helloworld/index/goodbye` 你将会得到两个完全相同的页面。

开始输出和getChildHtml方法

在Magento默认的配置下,HTML输出是从名为“root”的块开始(其实是因为这个块拥有output属性【译者注:任何一个拥有 output属性的块都是顶层块,在拥有多个顶层块的情况下Magento将按照块定义的先后顺序输出HTML】)。我们覆盖了“root”块的模板
template="../../../../../code/local/Alanstormdotcom/Helloworld/simple_page.phtml"
模板文件的查找路径是当前主题(theme)的根目录,Magento默认设置时这里
app/design/frontend/base/default

为页面加入内容

到目前为止,我们的页面都比较无聊,啥也没有。我们来为页面加点有意义的内容。修改local.xml如下
<helloworld_index_index> <reference name="root"> <block type="page/html" name="root" template="../../../../../code/local/Alanstormdotcom/Helloworld/simple_page.phtml"> <block type="customer/form_register" name="customer_form_register" template="customer/form/register.phtml"/> </block> </reference> </helloworld_index_index>
我们在“root”块里面嵌套了一个块“customer_form_register”。这个块是Magento本来就有的,包含了一张用户注册表单。 我们把这个块嵌套进来,那么我们在模板文件里面就能用这个块的内容。使用方法如下,修改simple_page.phtml
`
getChildHtml('customer_form_register'); ?>

` 这里“getChildHtml”的参数就是要引入的块的名字,使用起来相当方便。清空Magento缓存,刷新hello world页面,你应该在红色背景上看到用户注册表单。Magento还有一个块,叫做“top.links”,让我们把它也加进来。修改 simple_page.html `

Links

getChildHtml('top.links'); ?> getChildHtml('customer_form_register'); ?> ` 刷新页面,你会发现

Links

显示出来了,但是“top.links”什么都没有显示。那是因为我们并没有把 这个块引入到local.xml,所以Magento找不到这个块。“getChildHtml”的参数一定要是当前页面的布局文件中声明过的块。这样的 话Magento就可以只实例化需要用到的块,节省了资源,我们也可以根据需要为块设置不同的模板文件。

我们修改local.xml文件如下
<helloworld_index_index> <reference name="root"> <block type="page/html" name="root" template="../../../../../code/local/Alanstormdotcom/Helloworld/simple_page.phtml"> <block type="page/template_links" name="top.links"/> <block type="customer/form_register" name="customer_form_register" template="customer/form/register.phtml"/> </block> </reference> </helloworld_index_index>
清空Magento缓存,刷新页面,你会看到一排链接显示出来了。【译者注:如果你细心一点的话你会发现“top.links”块没有template属性,那是因为这个块的类中一定定义了默认的模板
protected function _construct() { $this->setTemplate(&#039;page/template/links.phtml&#039;); }

总结

这一章我们讲解了布局的基础知识。你可能会觉得这个很复杂,但是你也不必过分担心,因为平常使用Magento是不会用到这些知识 的,Magento提供的默认布局应该可以满足大部分需求。对于想要深入研究Magento的开发者来说,理解Magento的布局是至关重要的。布局, 块和模板构成了Magento MVC架构中的View,这也是Magento的特色之一。

深入理解Magento – 第二章 – Magento请求分发与控制器

深入理解Magento

作者:Alan Storm
翻译:Hailong Zhang

第二章 – Magento请求分发与控制器

Model-View-Controller (MVC) ,模型-视图-控制器,源于Smalltalk编程语言和Xerox Parc。现在有很多系统是基于MVC架构的,不同的系统MVC的实现也略有不同,但都体现了MVC的精髓,分离数据,业务逻辑和显示逻辑。最常见的 PHP MVC框架是这样的

  1. URL请求被一个PHP文件拦截,通常称为前端控制器(Front Controller)
  2. 这个PHP文件分析这个URL,获得一个执行控制器(Action Controller)的名字和一个执行方法(Action Method)的名字,这个过程通常称为路由(Routing)
  3. 实例化#2获得的执行控制器
  4. 调用执行控制器的执行方法
  5. 执行方法中处理业务逻辑,比如获取数据
  6. 执行控制器负责把数据传递给显示逻辑
  7. 显示逻辑生成HTML
    这个架构相对于传统的“每个php都是一个页面”来讲已经是一个巨大的飞跃,但还是有人抱怨【译者注: CodeIgniter就是这样一个MVC框架】
  • 前端控制器仍然以全局的方式运行

  • 基于配置的惯例导致了系统不够模块化

    • URL Routing不够灵活
    • 控制器往往和视图绑定
    • 更改默认设置往往导致大量的重构

Magento创造了一个更抽象的MVC来解决上述问题。

  1. URL请求被一个PHP拦截
  2. 这个PHP文件实例化一个Magento对象
  3. Magento对象实例化前端控制器
  4. 前端控制器实例化全局配置中指定的路由对象,可以是多个
  5. 路由对象会逐个与请求URL匹配
  6. 如果发现匹配,那么可以获得一个执行控制器和一个执行方法的名字
  7. 实例化#6获得的执行控制器,并调用相应的执行方法
  8. 执行方法中处理业务逻辑,模型数据
  9. 控制器实例化布局对象(Layout)
  10. 布局对象根据请求的参数,系统配置创建一个块对象(Block)列表,并实例化
  11. 布局对象会调用块对象的output方法生成HTML。这是一个递归的过程,因为块对象可以嵌套块对象
  12. 每一个块对象都和一个模板文件(Template File)对应。块对象包含了显示逻辑,模板文件包含了HTML和PHP输出代码
  13. 块对象直接从模型那里获得数据,换句话说,在Magento的MVC架构中,控制器并不直接把数据传给视图
    这里很复杂,我们以后会详细解释每一个部分。我们先关注“前端控制器->路由对象->执行控制器”部分。

Hello World示例

我们讲了太多理论,现在让我们来实践一下,通过实践来加深理解。下面是我们将要做的事情

  1. 创建一个Hello World模块
  2. 为这个模块配置路由
  3. 为这个模块创建执行控制器

创建Hello World模块

首先,我们要创建一个模块的目录结构,这个我们以前已经做过了,就不再熬述
app/code/local/Alanstormdotcom/Helloworld/Block app/code/local/Alanstormdotcom/Helloworld/controllers app/code/local/Alanstormdotcom/Helloworld/etc app/code/local/Alanstormdotcom/Helloworld/Helper app/code/local/Alanstormdotcom/Helloworld/Model app/code/local/Alanstormdotcom/Helloworld/sql
下面是config.xml的内容
PATH: app/code/local/Alanstormdotcom/Helloworld/etc/config.xml <config> <modules> <Alanstormdotcom_Helloworld> <version>0.1.0</version> </Alanstormdotcom_Helloworld> </modules> </config>
然后我们要创建一个系统配置文件来激活这个模块
PATH: app/etc/modules/Alanstormdotcom_Helloworld.xml <config> <modules> <Alanstormdotcom_Helloworld> <active>true</active> <codePool>local</codePool> </Alanstormdotcom_Helloworld> </modules> </config>
最后,让我们检查一下模块是不是已经被激活

  1. 清空Magento缓存
  2. 在管理后台,进入 System->Configuration->Advanced
  3. 展开“Disable Modules Output”
  4. 确认Alanstormdotcom_Helloworld显示出来了

配置路由

下面,我们要配置一个路由。路由是用来把一个URL请求转换成一个执行控制器和方法。和传统的PHP MVC不同的是,你需要在Magento的全局配置中显式的定义你的路由。我们继续上面的例子,在config.xml中,添加如下代码
<config> ... <frontend> <routers> <helloworld> <use>standard</use> <args> <module>Alanstormdotcom_Helloworld</module> <frontName>helloworld</frontName> </args> </helloworld> </routers> </frontend> ... </config>
在这里,我们有很多新名词要解释。

什么是

标签指向一个Magento区(Area),比如“frontend”就是指网站的前台,“admin”是指网站的后台,“install”是 指Magento的安装程序。【译者注:这个有点像磁盘分区,区和区之间是相互独立的,但是都归操作系统能够管理,在这里归Magento管理。默认的 Magento安装没有“install”这个区,frontend区接管了,全局配置中的以下代码可以解释这一点
<frontend> ... <install> <use>standard</use> <args> <module>Mage_Install</module> <frontName>install</frontName> </args> </install> ... </frontend>

什么是

Phil Karlton有一句很著名的话“在计算机领域只有两件事是困难的:缓存和命名”。Magento引入了很多新概念,无疑存在很多命名问题,这里就是一个 例子。标签有时候包含的是路由对象的定义,有时候包含的是路径的定义。路由对象是进行路由操作的实体,而路径仅仅是路由对 象的一个参数。【译者注: 如果你仔细看过那个全局配置xml的话,你会发现有两处地方出现,一处是“ -> ”,另外一处是“ -> ”。你再仔细看看会发现两处包含的内容不一样。第一处包含的是路由对象的定义,第二处包含 的是路径的定义。】

什么是

这个标签的内容应该是一个模块的全名,Packagename_Modulename,在这里是“Alanstormdotcom_Helloworld”。Magento用这个名字来定位你的模块文件。

什么是

当一个router解析一个URL的时候,它是按照如下规则进行的
http://example.com/frontName/actionControllerName/actionMethod/
所以,当我们在标签里定义了“helloworld”以后,Magento会把如下的URL请求交给我们的模块“Alanstormdotcom_Helloworld”来处理
http://example.com/helloworld/*
有些人容易把和前端控制器(Front Controller)混淆起来。它们是两个不同的概念,只跟路由相关。【译者注: 根据我们前面讲过的Magento的MVC流程,前端控制器是用来实例化所有路由的,而这里的“frontName”只是路由过程中的一个参数】

什么是

这个标签的名字应该是模块名字的小写版本。我们的模块名字是“Helloworld”,所以这里我们用“helloworld”。你应该也已经注意 到我们定义的“frontName”也是和我们的模块相匹配的。这是一个不成文的规定,但不是强制要求。事实上,一个模块可以定义多 个,也就是可以有多个“frontName”。

为路由创建执行控制器

还记得Magento的MVC流程吗?路由会把控制权交给执行控制器。上面我们定义了路由,现在我们来定义我们的执行控制器。首先创建文件
app/code/local/Alanstormdotcom/Helloworld/controllers/IndexController.php
模块的控制器应该放在模块的子目录“controllers”(小写c)里面。这是规定,Magento会在这个目录寻找模块的控制器文件。我们的第一个控制器包含以下内容
class Alanstormdotcom_Helloworld_IndexController extends Mage_Core_Controller_Front_Action { public function indexAction() { echo &#039;Hello World!&#039;; } }
清空Magento缓存,请求如下URL
http://exmaple.com/helloworld/index/index
如果你看到一个空白页面上面写着“Hello World”,那么恭喜你,你已经成功创建了你的第一个Magento控制器!

如何命名执行控制器?

还记得config.xml的标签吗?
<module>Alanstormdotcom_Helloworld</module>
执行控制的名字的构成如下

  1. 标签的内容开始 (Alanstormdotcom_Helloworld)
  2. 紧接一个下划线 (Alanstormdotcom_Helloworld_)
  3. 加上我们给控制器取的名字“Index”(Alanstormdotcom_Helloworld_Index)
  4. 最后加上关键词“Controller” (Alanstormdotcom_Helloworld_IndexController)
    我们自己定义的属于frontend区的执行控制器都应该继承Mage_Core_Controller_Front_Action。

URL里面的index/index是什么意思?

正如前文所述,Magento默认的路由的规则如下
http://example.com/frontName/actionControllerName/actionMethod/
所以在我们请求的URL
http://exmaple.com/helloworld/index/index
其中“helloworld”是“frontName”,第一个“index”是执行控制器(Action Controller)的名字,第二个“index”是执行方法的名字。对比我们写的执行控制器代码,我们不难发现执行方法的定义是执行方法名字加上 “Action”关键字
public function indexAction(){...}
Magento根据命名规则找到执行控制器文件并实例化,然后再根据命名规则调用指定的执行方法。如果URL没有给出执行控制器名字或者执行方法,Magento会用默认的“index”来替代,所以下面三个URL是等价的
http://exmaple.com/helloworld/index/index http://exmaple.com/helloworld/index/ http://exmaple.com/helloworld/
我们再来看一个例子。如果URL如下
http://exmaple.com/checkout/cart/add
Magento的执行步骤如下

  1. 查询全局配置,找到frontName “checkout”对应的模块,Mage_Checkout
  2. 找到执行控制器 “Mage_Checkout_CartController”
  3. 调用执行控制器的“addAction”方法

进一步理解执行控制器

下面我们来为我们的执行控制器添加一个执行方法。添加如下代码到IndexController.php
public function goodbyeAction() { echo &#039;Goodbye World!&#039;; }
请求URL
http://example.com/helloworld/index/goodbye
这次你应该看到“Goodbye World!”。因为我们继承了“Mage_Core_Controller_Front_Action”,我们可以使用一些父类已经定义好的方法和变 量。比如父类会把URL后面跟的参数转换成key/value的数组。添加如下代码到我们的执行控制器
public function paramsAction() { echo &#039;<dl>&#039;; foreach($this->getRequest()->getParams() as $key=>$value) { echo &#039;<dt>**Param: **&#039;.$key.&#039;</dt>&#039;; echo &#039;<dl>**Value: **&#039;.$value.&#039;</dl>&#039;; } echo &#039;</dl>&#039;; }
请求如下URL
http://example.com/helloworld/index/params?foo=bar&baz=eof
你应该看到如下输出
Param: foo
Value: bar
Param: baz
Value: eof
最后,让我们再写一个执行控制器,用来处理以下URL
http://example.com/helloworld/messages/goodbye
这里的执行控制器名字是“messages”,所以我们要创建如下文件
app/code/local/Alanstormdotcom/Helloworld/controllers/MessagesController.php
执行控制器的类名应该是
Alanstormdotcom_Helloworld_MessagesController
添加执行方法
public function goodbyeAction() { echo &#039;Another Goodbye&#039;; }

好了,Magento的MVC架构大概就是这样了。它比传统的PHP MVC要复杂一点,但是Magento的这个高度灵活的MVC架构能让你创造出几乎所有你能想到的URL结构。

http://www.zhlmmc.com/?p=587

深入理解Magento – 第一章 – Magento强大的配置系统

深入理解Magento

作者:Alan Storm
翻译:Hailong Zhang

第一章 – Magento强大的配置系统

Magento的配置系统就像是Magento的心脏,支撑着Magento的运行。这套配置系统掌管着几乎所有“module/model /class/template/etc”。它把整个Magento系统抽象出来,用一个配置文件来描述。这里的“配置文件”并不是一个物理上存在的文 件,而是Magento根据当前的系统状态动态生成的一段XML。大多数的PHP开发者并不习惯于这样抽象层,因为它增加的编程的复杂性。但是这样的抽象 提供了无与伦比的灵活性,允许你覆盖几乎任何系统的默认行为。

首先,让我们写一个简单的插件来看看这个所谓的“配置文件”长什么样。虽然我已经提供的现成的代码,但是还是建议你自己建立这个插件,把整个流程走一遍有助于你的理解。

设置插件的目录结构

我们将要创建一个Magento的模块【译者注: Magento的插件不叫plug-in,叫module,翻译成模块】。Magento的模块由php和xml文件组成,目的是扩展或者覆盖系统的行 为,比如为订单增加数据模型,更改一个类的方法,或者增加一个全新的功能。【译者注:Magento自带的那些功能也都是基于模块的,比如用户注册,商品 展示,结账流程等等。Magento给我的感觉就是一切皆模块,和Eclipse的插件体系结构有点像】

大多数Magento的系统模块的结构和我们将要构建的插件的结构是一样的。Magento的系统模块在以下目录
app/code/core/Mage
每一个子目录都是一个单独的模块。这些模块是由Magento官方开发的。我们安装完Magento以后,所使用的功能就是来自这些模块。我们自己创建的模块应该放在如下目录
app/code/local/Packagename
“Packagename”应该是一个唯一的字符串,用来标识你的代码。通常人们使用公司名字作为Packagename,比如
app/code/local/Microsoft
由于我在做我自己的Magento项目,我将使用我自己的域名“Alanstormdotcom”。 然后,我们要创建以下目录结构
app/code/local/Alanstormdotcom/Configviewer/Block app/code/local/Alanstormdotcom/Configviewer/controllers app/code/local/Alanstormdotcom/Configviewer/etc app/code/local/Alanstormdotcom/Configviewer/Helper app/code/local/Alanstormdotcom/Configviewer/Model app/code/local/Alanstormdotcom/Configviewer/sql
你的插件并不一定需要包含以上所有的目录,但是为了以后开发方便,我们还是在一开始就把目录创建好。接下来我们要创建两个文件,一个是config.xml,放在etc目录下面
app/code/local/Alanstormdotcom/Configviewer/etc/config.xml
文件内容如下
<config> <modules> <Alanstormdotcom_Configviewer> <version>0.1.0</version> </Alanstormdotcom_Configviewer> </modules> </config>
第二个文件需要在如下位置创建
app/etc/modules/Alanstormdotcom_Configviewer.xml
第二个文件应该遵循如下命名规则“Packagename_Modulename.xml”,文件内容如下
<config> <modules> <Alanstormdotcom_Configviewer> <active>true</active> <codePool>local</codePool> </Alanstormdotcom_Configviewer> </modules> </config>
我们先不管这些文件是干什么的,以后会解释。建立好这两个文件以后,你的模块的骨架就已经完成了。Magento已经知道你的模块存在,但是现在你的模块不会做任何事情。我们来确认一下Magento确实装载了你的模块

  1. 清空Magento缓存
  2. 在后台管理界面,进入 System->Configuration->Advanced
  3. 展开“Disable Modules Output”
  4. 确认“Alanstormdotcom_Configviewer”显示出来了
    如果你看到“Alanstormdotcom_Configviewer”,那么恭喜你,你已经成功创建了你第一个Magento模块!

创建模块逻辑

我们之前创建的模块不会做任何事情,下面我们来为这个模块加入逻辑
1. 检查“showConfig”查询字符串是否存在
2. 如果“showConfig”存在,那么检查“showConfigFormat”查询字符串是否存在
3. 如果“showConfigFormat”存在,那么输出指定格式的配置信息,否则输出默认格式的配置信息
4. 终止执行流程
首先更改我们的config.xml文件
<config> <modules>...</modules> <global> <events> <controller_front_init_routers> <observers> <alanstormdotcom_configviewer_model_observer> <type>singleton</type> <class>Alanstormdotcom_Configviewer_Model_Observer</class> <method>checkForConfigRequest</method> </alanstormdotcom_configviewer_model_observer> </observers> </controller_front_init_routers> </events> </global> </config>
然后创建如下文件
Alanstormdotcom/Configviewer/Model/Observer.php
输入以下内容
<?php class Alanstormdotcom_Configviewer_Model_Observer { const FLAG_SHOW_CONFIG = &#039;showConfig&#039;; const FLAG_SHOW_CONFIG_FORMAT = &#039;showConfigFormat&#039;; private $request; public function checkForConfigRequest($observer) { $this->request = $observer->getEvent()->getData(&#039;front&#039;)->getRequest(); if($this->request->{self::FLAG_SHOW_CONFIG} === &#039;true&#039;){ $this->setHeader(); $this->outputConfig(); } } private function setHeader() { $format = isset($this->request->{self::FLAG_SHOW_CONFIG_FORMAT}) ? $this->request->{self::FLAG_SHOW_CONFIG_FORMAT} : &#039;xml&#039;; switch($format){ case &#039;text&#039;: header("Content-Type: text/plain"); break; default: header("Content-Type: text/xml"); } } private function outputConfig() { die(Mage::app()->getConfig()->getNode()->asXML()); } } ?>
好了,代码编辑结束。清空你的Magento缓存,输入如下URL
http://magento.example.com/?showConfig=true
【译者注: 根据文中的配置,不难看出任何指向Magento的URL加了“?showConfig=true”以后,都会输出同样的内容,正常的执行流程会被终止。】

配置文件分析

打开上述URL,你应该看到一个巨大的XML文件。这个文件描述了当前Magento系统的状态。它列出了所有的模块,数据模型,类,事件,监听器等等。举个例子,如果你搜索如下字符串
Configviewer_Model_Observer
你会发现刚刚你创建的那个类被列出来了。Magento会解析每个模块的config.xml,并把它们包含在这个全局配置中。

这个配置文件有啥用?

到目前为止,我们所作的事情似乎没什么意义,但是这个配置文件却是理解Magento的关键因素。你创建的每一个模块都会被加到这个配置文件中,任 何时候,你需要调用一个系统功能的时候,Magento都会通过这个配置文件来查询相应的模块和功能。举个简单的例子,如果你懂MVC的话,你应该和 “helper class”之类概念的打过交道
$helper_salesrule = new Mage_SalesRule_Helper();
Magento抽象了PHP的类声明方式。在Magento系统中,上面的代码等同于
$helper_salesrule = Mage::helper(&#039;salesrule&#039;);
Magento将通过以下逻辑来处理这行代码

  1. 在配置文件中查找标签
  2. 里面查找 标签
  3. 里面查找 标签
  4. 实例化从#3找到的类(Mage_SalesRule_Helper)
    Magento总是通过配置文件来获得类名,这个逻辑看起来有些复杂,但这样做的优点也很明显,我们可以不需要更改Magento的代码就能更改 Magento的核心功能。【译者注: 在这个例子中,我们可以通过修改配置文件用我们自己的SalesRule_Helper类来替换原来那个】这种高度抽象的编程方式在php中并不常见,但 是它可以让你清晰的扩展或者替换系统的某一部分。

深入理解Magento – 前言

深入理解Magento

作者:Alan Storm
翻译:Hailong Zhang

前言

我从2007年开始使用Magento,应该算是国内第一批使用Magento的用户。但是我却从来没有认真研究过Magento,更多的停留在应用层面。虽然也做过一些插件,但也就是依葫芦画瓢而已。偶然间看到 Alan Storm 的一系列关于Magento的文章, 我忍不住的心潮澎湃,相见恨晚。Alan的文章循序渐进,深入浅出地讲述了Magento的架构和工作方式, 把一个复杂系统的内部结构淋漓尽致的展现在我们面前。读完以后,我茅塞顿开,感叹Magento的强大,架构之美。于是我决定翻译Alan的文章,希望对 更多的Magento开发者有所帮助。

Alan的文章不是Magento入门教程,并不是教你如何安装,使用Magento。借用 Alan的原话“Magento Articles for Professional Developers”,我认为这里的“Professional”包含以下几点:

  1. 懂得PHP的运作方式
  2. 良好的编程基础 (写过5w行代码以上)
  3. 了解基本设计模式
  4. 安装并使用过Magento
  5. 阅读并修改过Magento代码

我的翻译加入了我自己的理解,对原文有所修改和调整。还有,Alan的文章写的比较早,现在Magento出新版了,所以我对文中的代码进行了修 改,我翻译以后的文章是基于Magento 1.4.0.1版本的。所以如果你发现我的翻译和原文有不符的地方,请不要见怪。下面让我们开始奇妙的Magento之旅!

原文地址:http://www.zhlmmc.com/?p=576

Magento学习资源

最近开始学习magento,整理一些学习资源和教程。

magento用户手册中文版

magento模板制作教程中文版

深入理解magento

深入理解magento是官方提供的由Alan Storm撰写的开发人员教程,下面是Hailong Zhang翻译的深入理解maganto系列教程,Alan Storm的原文还有四章,该系列教程介绍了magento开发的一些基础知识。

* 11.[Magento Default System Configuration Values](http://alanstorm.com/magento_default_system_configuration_values) 深入理解magento系列教程还有由[精东](http://www.wemvc.com)翻译的第九章和第十章,不过顺序好像是反了。

**
**

extension tutorial系列由 Johannes Teitge ** **撰写,其中介绍了magento的前台和后台扩展方法,包括后台Grid对象的使用和多语言的用法,原文德语,只需按照例子操作即可。

其它资源