上一篇开发实例中作者讲过了Magento的MVC分层中C(控制器)层的规则。Magento的M(模型)和V(视图)层和传统的MVC有很大的改进。
传统的MVC分层是通过执行控制器内的方法,控制器的方法内获取不同的模型数据,然后传递给视图。
Magento却不同了,Magento是从视图中直接引用模型中的数据,这样就导致视图被拆成块(Block)和模板(Template)两部分,块是PHP对象,而模板是原始PHP文件,混合了XHTML和PHP代码(也就是把PHP作为模板语言来使用了)。每一个块都和一个唯一的模板文件绑定。在模板文件phtml中,“$this”就是指该模板文件对应的块对象。
比如:app/design/frontend/base/default/template/catalog/product/list.phtml文件中的
<?php $_productCollection=$this->getLoadedProductCollection() ?>
这里“getLoadedProductCollection”方法可以在这个模板的块对象“Mage_Catalog_Block_Product_List”中找到
我们看看块(Block)文件
app/code/core/Mage/Catalog/Block/Product/List.php
public function getLoadedProductCollection()
{
return $this->_getProductCollection();
}
…
块的“_getProductCollection”方法会实例化模型,并读取数据然后返回给模板。
块的嵌套::
Magento把视图分离成块和模板的真正强大之处在于“getChildHtml”方法。这个方法可以让你实现在块中嵌套块的功能。顶层的块调用第二层的块,然后是第三层……这就是Magento如何输出HTML的。
我们可以看到这个模板里面很多地调用了“$this->getChildHtml(…)”。每次调用都会引入另外一个块的HTML内容,直到最底层的块。
布局对象::
看到这里,你可能有这样的疑问
Magento怎么知道在一个页面上要用那些块?
Magento怎么知道哪一个块是顶层块?
“$this->getChildHtml(…)”里面的参数是什么意思?块的名字吗?
Magento引入了布局对象(Layout Object)来解决上面的那些问题。布局对象(或者说布局文件)就是一个XML文件,定义了一个页面包含了哪些块,并且定义了哪个块是顶层块。
在上一节在执行方法(Action Method)里面直接输出了HTML内容。现在我们要为我们的Mymodule模块创建一个简单的HTML模板。首先我们要创建如下布局文件
app/design/frontend/default/default/layout/local.xml
<?xml version=”1.0″ encoding=”UTF-8″?>
<layout version=”0.1.0″>
<mymodule_index_index>
<reference name=”root”>
<block type=”page/html” name=”root” output=”toHtml” template=”mymodule/home.phtml”/>
</reference>
</mymodule_index_index>
</layout>
然后我们在模板内创建个mymodule模块(小写)的目录,新增一个home.phtml文件并且写入
<head>
<title>Untitled</title>
<style type=”text/css”>
body {
background-color:#f00;
}
</style>
</head>
<body>
<h4>Hello Word</h4>
</body>
</html>
最后,我们要在执行控制器里面调用布局文件,开始输出HTML。
app/code/local/Qxz/Mymodule/controllers/IndexContrllers.php
修改function indexAction()的方法:
$this->loadLayout();
$this->renderLayout();
清空缓存并刷新。访问127.0.0.1/mymodule,就可以看到纯红色背景的页面。
我们看到执行控制器的indexAction方法里调用了loadLayout和renderLayout
Magento是如何处理的呢?
首先调用“loadLayout”时,Magento会生成这个布局文件。
为每一个block和reference标签实例化一个块对象。
注:在local.xml的layout布局文件中
块对象的类名是通过标签的name属性来查找的。这些块对象被存储在布局对象的_blocks数组中
如果block标签包含了output属性,那么这个块的名字和output属性的值会被添加到布局对象的_output数组中
然后,当你在执行方法中调用“renderLayout”方法时,Magento会遍历_output数组中所有的块名字,从_blocks数组中获得该名字的块,并调用块对象中使用output属性的值作为名字的函数。这个函数往往是“toHtml”。这个output属性也告诉Magento这里就是输出HTML的起点,也就是顶层块。
#更多可以查看app/code/core/Mage/Core/Model/Layout.php文件
那么对象是如何被实例化的,这个布局文件时如何被生成的,最后我们来实践下。
实例化块对象
在布局文件中,block和reference标签有一个“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),这些类都用相似的方法被实例化。
block和reference的区别
我们上面提到block和reference都会实例化块对象,那么它们究竟有什么区别呢? reference在布局文件中是用来表示替换一个已经存在的块。
<block type=”page/html” name=”root” output=”toHtml” template=”page/one.phtml”>
</block>
<reference name=”root”>
<block type=”page/someothertype” name=”root” template=”page/two.phtml” />
</reference>
Magento首先创建了一个名叫“root”的块。然后,它有发现了一个引用(reference)的名字也叫“root”,Magento会把原来那个“root”块替换成reference标签里面的那个快。在这里,块“root”被我们用reference替换了,指向了一个不同的模板文件。
布局文件是如何生成的
现在我们对布局文件已经有所了解了,但是这个布局文件是那里来的呢?要回答这个问题,我们得引入Magento中的另外两个概念,操作(Handle)和包布局(Package Layout)。
我们来试下给Mymodule模块的IndexController.php文件增加个blog的方法
function blogAction()
{
$this->loadLayout();
$this->renderLayout();
}
同样我们调用布局
然后修改local.xml文件
红色为新加的,注意书写方法包含在<layout>里,对称包含。
<?xml version=”1.0″ encoding=”UTF-8″?>
<layout version=”0.1.0″>
<mymodule_index_index>
<reference name=”root”>
<block type=”page/html” name=”root” output=”toHtml” template=”mymodule/home.phtml”/>
</reference>
</mymodule_index_index>
<mymodule_index_blog>
<reference name=”root”>
<block type=”page/html” name=”root” output=”toHtml” template=”mymodule/blog.phtml”/>
</reference>
</mymodule_index_blog>
</layout>
这时候我们如果访问
127.0.0.1/mymodule/index/index
127.0.0.1/mymodule/index/blog
会输出不同的视图页面。
开始输出和getChildHtml方法
在Magento默认的配置下,HTML输出是从名为“root”的块开始(其实是因为这个块拥有output属性
注:任何一个拥有output属性的块都是顶层块,在拥有多个顶层块的情况下Magento将按照块定义的先后顺序输出HTML。
我们覆盖了“root”块的模板template=”mymodule/home.phtml”
模板文件的查找路径是当前主题(theme)的根目录,Magento默认设置是在
app/design/frontend/base/default 目录里的template目录
1.9后的版本为app/design/frontend/rwd/default
我们为执行控制器IndexController.php增加一个注册的方法;
function registerAction() #注册方法
{
$this->loadLayout();
$this->renderLayout();
}
既然用到了布局,布局文件肯定也要修改了,修改local.xml,添加个mymodule_index_register块
<mymodule_index_register>
<reference name=”root”>
<block type=”customer/form_register” name=”register” output=”toHtml” template=”customer/form/register.phtml”/>
</reference>
</mymodule_index_register> </mymodule_index_register>然后我们在mymodule模板目录里增加个register.phtml文件哦
写入下面代码:
<?php
echo $this->getChildHtml(‘register’);
#在这里会直接调用block块的name=register的模板
?>
这里“getChildHtml”的参数就是要引入的块的名字,使用起来相当方便。
清空Magento缓存并刷新,重新访问127.0.0.1/mymodule/index/register就可以看到:
“getChildHtml”的参数一定要是当前页面的布局文件中声明过的块。这样的话Magento就可以只实例化需要用到的块,节省了资源,我们也可以根据需要为块设置不同的模板文件。
布局,块和模板构成了Magento MVC架构中的View,这是Magento的特色。
针对部分块没有template模板属性的,可能是块的类中定义了默认的模板
比如$this->setTemplate(‘page/template/links.phtml’);
总结:
我们看看coloa.xml布局文件的数据:
<?xml version=”1.0″ encoding=”UTF-8″?>
<layout version=”0.1.0″>
<mymodule_index_index>
<reference name=”root”>
<block type=”page/html” name=”root” output=”toHtml” template=”mymodule/home.phtml”/>
</reference>
</mymodule_index_index>
<mymodule_index_blog>
<reference name=”root”>
<block type=”page/html” name=”root” output=”toHtml” template=”mymodule/blog.phtml”/>
</reference>
</mymodule_index_blog>
<mymodule_index_register>
<reference name=”root”>
<block type=”customer/form_register” name=”register” output=”toHtml” template=”customer/form/register.phtml”/>
</reference>
</mymodule_index_register>
</layout>
作者用3种不同的颜色来区分了。至于这里的type=”customer/form_register”,应该是引用客户的注册表单吧。作者也是自学开发,以后更精通了再跟进。
关于作者