Magento 的布局(Layout),块(Block)和模板(Template)
什么是blcok
和之前将的magento mvc架构,我们接下来应该讲模型(model),但是我们跳过模型先来看布局块.和一些流行的php mvc架构不同的是,magento的控制器不直接将数据传输给视图,相反视图将直接引用模型,从模型读取数据.这样的设计就导致了视图被拆分成俩部分,块(Block)和模板(Template).块是php对象,而模板是原始php文件,混合了xhtml和php代码(也就把php作为模板语言来使用了.)每一个块和一个唯一的模板文件绑定在模板文件phtml中,$this就是指该模板文件对于的Block对象
什么是Block,block的基本格式如下
<block
type="catalog/product_view"
name="product.info"
as="product-info"
template="catalog/product/view.phtml"
after="-"
/>
block | 块笔记元素 |
---|---|
type | 是指teplate中将要调用的处理代码位置$this |
name | 是当前给的block定一个名字,一般用在layout定义中进行引用 |
as | 可以认为是别名,主要定义在大的架构模板中调用时的名字 |
template | 指定当前block的模板文件路径 |
after | 指定当前block与同层次block之间的顺序,代表在xxx block之后 |
before | 制定当前block与同层次block之间的顺序,代表在xx block之前 |
output | toHtml |
举例
app/design/frontend/base/default/template/catalog/product/list.phtml
你将看到如下源码
<?php $_productCollection=$this->getLoadedProductCollection() ?>
<?php if(!$_productCollection->count()): ?>
<p class="note-msg"><?php echo $this->__('There are no products matching the selection.') ?></p>
<?php else: ?>
这里getLoadedProductCollection方法可以在这个模板对象Mage_catalog_Block_Product_List中找到
app/code/core/Mage/Catalog/Block/Product/List.php
public function getLoadedProductCollection()
{
return $this->_getProductCollection();
}
其中的_getProductCollection方法会实例化模型,并读取数据然后返回模板
嵌套块
magento把视图分离成块和模板的真正强大之处"getChildHtml"方法.这个方法.这个方法可以让你实现功能.顶层的块调用第二层的块,然后是第三层...这就是magento如何输出html的
app/design/frontend/base/default/template/page/1column.phtm
<?php
/**
* Template for Mage_Page_Block_Html
*/
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $this->getLang() ?>" lang="<?php echo
$this->getLang() ?>">
<head>
<?php echo $this->getChildHtml('head') ?>
</head>
<body<?php echo $this->getBodyClass()?' class="'.$this->getBodyClass().'"':'' ?>>
<?php echo $this->getChildHtml('after_body_start') ?>
<div class="wrapper">
<?php echo $this->getChildHtml('global_notices') ?>
<div class="page">
<?php echo $this->getChildHtml('header') ?>
<div class="main-container col1-layout">
<div class="main">
<?php echo $this->getChildHtml('breadcrumbs') ?>
<div class="col-main">
<?php echo $this->getChildHtml('global_messages') ?>
<?php echo $this->getChildHtml('content') ?>
</div>
</div>
</div>
<?php echo $this->getChildHtml('footer') ?>
<?php echo $this->getChildHtml('before_body_end') ?>
</div>
</div>
<?php echo $this->getAbsoluteFooter() ?>
</body>
</html>
我们可以看到这个模板里面很多地方调用了$this->getChildHtml(...)每次调用都会引入另外一个块的html内容,直到最底层的块.
对象布局
看到这里你可能有这样的疑问
1) Magento 怎么知道在一个页面上要用那些块?
2) Magento 怎么知道哪一个块是顶层块?
3)$this->getChildHtml(…)”里面的参数是什么意思?块的名字吗
magento引入了布局对象(Layout Object)来解决上面那些问题.布局对象(或者说布局文件)就是一个XML文件,里面定义了一个页面包含了哪些模块,并且定义了哪个模块是顶层模
在第二章的时候我们在执行方法(Action Method)里面输出了html内容.现在我们要为我们的hello world模块创建一个简单的html模板
首先我们需要创建如下文件
app/design/frontend/default/layout/local.xml
包含以下内容
<?xml version="1.0" encoding="UTF-8"?>
<layout version="0.1.0">
<helloworld_index_index>
<reference name="root">
<block type="page/html" name="root" output="tohtml" template="helloworld/simple_page.phtml"></block>
</reference>
</helloworld_index_index>
</layout>
再创建如下文件
app/design/frontend/default/teplate/helloworld/simple_page.phtml
包含以下内容
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Untitled</title>
<style type="text/css">
body {
background-color:#f00;
}
</style>
</head>
<body>
<h4>Links</h4>
<?php echo $this->getChildHtml('top.links'); ?>
<?php echo $this->getChildHtml('customer_form_register'); ?>
</body>
</html>
最后我们要在控制器里面调用布局文件,开始输出html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Untitled</title>
<style type="text/css">
body {
background-color:#f00;
}
</style>
</head>
<body>
<h4>Links</h4>
<?php echo $this->getChildHtml('top.links'); ?>
<?php echo $this->getChildHtml('customer_form_register'); ?>
</body>
</html>
最后,我们要在控制器里面调用布局文件,开始输出 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 Viewr模块,这和我们第一章将的config viewer模块很相似,都是查看magento的内部信息.安装完这个模块之后
打开如下url
http://example.com/helloworld/index/index?showLayout=pag
你看到的是你正在请求的页面的布局文件。它是由 block,reference 和 remove 组成的。当你在执行方法中调用
“loadLayout”时,Magento 会做如下处理:
生成这个布局文件
为每一个block和reference标签实例化一个块对象.快对象的类名是通过标签的name属性来茶查找的.这些对象被存储在布局对象的_blocks数组中.
如果block标签包含了output属性,那么这个块的名字和output属性的值会被添加到_output数组中.
然后当你在执行方法调用readerLayout方法时,magento会遍历_output数组中所有的块名字,从_blocks数组汇总获得该名字的块,并调用块对象中使用output属性的值作为名字的函数.这个函数往往是toHtml.这个output属性也告诉magento这里就是html输出html的起点,也就是顶层块
实例化快对象
在布局文件中,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中被称为分组类名,这些类都用相似的方法被实例化.我们将在以后的章节介绍这个概念
block和reference的区别
我们上面提到block和reference都会实例化块对象,那么它们究竟有什么区别呢,reference在布局文件中是用来表示替换一个已经存在的块。
<block type="page/html" name="root" output="toHtml" template="page/2columns-left.phtml">
<!-- ... sub blocks ... -->
</block>
<!-- ... -->
<reference name="root">
<block type="page/someothertype" name="root" template="path/to/some/other/template" />
<!-- ... sub blocks ... -->
</reference>
magento首先创建了一个名叫root的块。然后又发现了一个引用reference的名字也叫root,Magento会把原来那个“root“块替换成reference标签里面的那个块
再来看看我们之前创建的那个local.xml
<layout version="0.1.0">
<default>
<reference name="root">
<block type="page/html" name="root" output="toHtml" template="helloworld/simple_page.phtml" />
</reference>
</default>
</layout>
在这里,块root被我妈用reference替换了,指向了一个不同的模板文件
布局文件是如何生成的
操作
现在我们对布局文件有所了解了,但是这个布局文件是哪里来的呢?要回答这个问题,我们得先引入magento中的另外俩个概念,操作(Handle)和包布局(Package Layout)
操作
magento会为每一个页面请求生成几个不同的操作.我们的layoutview模块可以显示这些处理器
http://example.com/helloworld/index/index?showLayout=handles
你应该看到类似如下的列表(和你的配置有关):
Handles For This Request
1. default
2. STORE_default
3. THEME_frontend_default_gap
4. helloworld_index_index
5. 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