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”一模一样。
究竟是怎么回事呢?
也许你看到这里一头雾水,没关系,我们来慢慢解释。首先你得安装一个 MagentoLayoutViewer.tar->Layout Viewer 模块,这和我们第一章讲的 Config Viewer 模块很相似,都是查看Magento的内部信息。安装完这个模块之后【注:你需要参照第一章的内容,为这个模块创建“app/etc/modules/App_Layoutviewer.xml”】,打开如下URL

http://example.com/helloworld/index/index?showLayout=page

你看到的是你正在请求的页面的布局文件。它是由block,reference和remove组成的。当你在执行方法中调用“loadLayout”时,Magento会做如下处理
生成这个布局文件
为每一个block和reference标签实例化一个块对象。块对象的类名是通过标签的name属性来查找的。这些块对象被存储在布局对象的_blocks数组中
如果block标签包含了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 = '';

if (!empty($this->_output)) {

foreach ($this->_output as $callback) {

$out .= $this->getBlock($callback[0])->$callback[1]();

}

}

return $out;

}

从这里我们也可以看出,一个页面的布局文件可以拥有多个顶层块。】
下面我们要讲解块对象是如何被实例化的,这个布局文件时如何被生成的,最后我们将动手做一个例子来实践这一章讲的内容。
实例化块对象
在布局文件中,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/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会为每一个页面请求生成几个不同的操作。我们的Layout View模块可以显示这些处理器

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

包布局
包布局和我们以前讲过的全局配置有些相似。它是一个巨大的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/

其实在全局配置中,有一个updates节点下面定义了所有将被装载的布局文件

<layout>

<updates>

<core>

<file>core.xml</file>

</core>

<page>

<file>page.xml</file>

</page>

...

</updates>

</layout>

当这些文件被装载以后,Magento还会装载最后一个布局文件,helloworld.xml,也就是我们之前新建的那个文件。我们可以通过这个文件来定制Magento的布局。
结合操作和包布局
在包布局文件中,我们可以看到一些熟悉的标签block,reference等等,但是他们都包含在一下这些标签中

<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_account_index>

<!-- ... -->

<update handle="customer_account"/>

!-- ... -->

</customer_account_index>

这段代码的意思是,如果一个请求包含了“customer_acount_index”操作,那么这个请求的布局文件也应该包含“customer_account”操作标签下面的block和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>

我们用一个引用(reference)覆盖了名为“root”的块。然后定义了一个新的块,指向了一个不同的模板文件。我们把这个引用放在default操作标签下面,那就说明这个Layout将对所有的请求有效。如果你访问Magento自带的一些页面,你会发现它们要么是空白,要么就是和我们“hello world”例子的红色背景,但这并不是我们想要的效果。我们来修改一下local.xml,让我们的模板仅对“helloworld”的请求有效。

<layout version="0.1.0">

<helloworld_index_index>

<reference name="root">

<block type="page/html" name="root" output="toHtml" template="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”请求我们要套用的布局是一样的,所以我们将用标签来重用已有的配置

<layout version="0.1.0">

<!-- ... -->

<helloworld_index_goodbye>

<update handle="helloworld_index_index" />

</helloworld_index_goodbye>

</layout>

清空Magento缓存,请求以下URL

http://example.com/helloworld/index/index

http://example.com/helloworld/index/goodbye

你将会得到两个完全相同的页面。
开始输出和getChildHtml方法
在Magento默认的配置下,HTML输出是从名为“root”的块开始(其实是因为这个块拥有output属性【注:任何一个拥有output属性的块都是顶层块,在拥有多个顶层块的情况下Magento将按照块定义的先后顺序输出HTML】)。我们覆盖了“root”块的模板

template="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="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

<body>

<?php echo $this->getChildHtml('customer_form_register'); ?>

</body>

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

<body>

<h1>Links</h1>

< ?php echo $this->getChildHtml('top.links'); ?>

< ?php echo $this->getChildHtml('customer_form_register'); ?>

</body>

刷新页面,你会发现Links显示出来了,但是“top.links”什么都没有显示。那是因为我们并没有把这个块引入到local.xml,所以Magento找不到这个块。“getChildHtml”的参数一定要是当前页面的布局文件中声明过的块。这样的话Magento就可以只实例化需要用到的块,节省了资源,我们也可以根据需要为块设置不同的模板文件。
我们修改helloworld.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 type="customer/form_register" name="customer_form_register" template="customer/form/register.phtml"/>

<block type="page/template_links" name="top.links"/>

</block>

</reference>

</helloworld_index_index>

</layout>

清空Magento缓存,刷新页面,你会看到一排链接显示出来了。【注:如果你细心一点的话你会发现“top.links”块没有template属性,那是因为这个块的类中一定定义了默认的模板

protected function _construct()

{

$this->setTemplate('page/template/links.phtml');

}


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

Magento的布局(Layout),块(Block)和模板(Template)的更多相关文章

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

    深入理解Magento 作者:Alan Storm 翻译:Hailong Zhang 第三章 – 布局,块和模板 我们接着研究Magento.根据我们第二章讲的Magento MVC的架构,我们接下来 ...

  2. Magento布局layout.xml文件详解

    解析顺序 布局xml文件一般位于app/design/{area}/{package}/{theme}/layout/目录下.Layout文件一般包含block.reference.action三种标 ...

  3. Django---MTV和MVC的了解,Django的模版语言变量和逻辑,常见的模板语言过滤器,自定义过滤器,CSRF了解,Django的母版(继承extends,块block,组件include,静态文件的加载load static),自定义simple_tag和inclusion_tag

    Django---MTV和MVC的了解,Django的模版语言变量和逻辑,常见的模板语言过滤器,自定义过滤器,CSRF了解,Django的母版(继承extends,块block,组件include,静 ...

  4. magento的布局文件之谜

    magento的布局文件layout.xml文件详解 解析顺序 布局xml文件一般位于app/design/{area}/{package}/{theme}/layout/目录下.Layout文件一般 ...

  5. 转载:SharePoint:扩展DVWP - 第1部分:布局增强 – 在默认值模板和编辑模板中重新排列栏

    SharePoint:扩展DVWP - 第1部分:布局增强 – 在默认值模板和编辑模板中重新排列栏 当我们在数据视图中启用编辑,删除模式的链接时,SPD总是将链接添加到左边. 而我本来希望添加到右侧. ...

  6. Duilib源码分析(五)UI布局—Layout与各子控件

    接下来,继续分析duilib之UI布局Layout,目前提供的布局有:VerticalLayout.HorizontalLayout.TileLayout.TabLayout.ChildLayout分 ...

  7. 04OC之分类Category,协议Protocol,Copy,代码块block

    一.Protocol协议 我们都知道,在C#有个规范称之为接口,就是规范一系列的行为,事物.在C#中是使用Interface关键字来声明一个接口的,但是在OC中interface是用来声明类,所以用了 ...

  8. [转]iOS代码块Block

    代码块Block是苹果在iOS4开始引入的对C语言的扩展,用来实现匿名函数的特性,Block是一种特殊的数据类型,其可以正常定义变量.作为参数.作为返回值,特殊地,Block还可以保存一段代码,在需要 ...

  9. iOS学习之代码块(Block)

    代码块(Block) (1)主要作用:将一段代码保存起来,在需要的地方调用即可. (2)全局变量在代码块中的使用: 全局变量可以在代码块中使用,同时也可以被改变,代码片段如下: ;//注意:全局变量 ...

随机推荐

  1. log4net面面观之Repository

    转:http://itrust.cnblogs.com/archive/2006/07/17/452895.html 上回说道:Repository可以说成基于一个log4net配置节创建的log4n ...

  2. 【Android】Android程序保护与破解浅析

    此文源自组内成员分享的PPT,其他成员的文档由于没有得到授权,暂不公开. 本文命令如果没有特殊注明,均为windows 7环境. 本文只涉及大概的知识点,不涉及具体的细节,需要注意. 反编译 apkt ...

  3. GTK+中的构件II(Widgets)

    GTK+中的构件II(Widgets) GTK+中的构件II(Widgets) 在本章的GTK+程序设计中,我们仍然要继续向大家介绍和展示各种各样的构件. GtkComboBox GtkComboBo ...

  4. 解决Mac下Sequel Pro 1.1 连接 Homebrew安装Mysql5.7.8的问题 Sequel Pro 1.1 encountered an unexpected error

    解决Mac下Sequel Pro 1.1 连接 Homebrew安装Mysql5.7.8的问题 Sequel Pro encountered an unexpected error Sequel Pr ...

  5. POJ2407–Relatives(欧拉函数)

    题目大意 给定一个正整数n,要求你求出所有小于n的正整数当中与n互质的数的个数 题解 欧拉函数模板题~~~因为n过大~~~所以直接用公式求 代码: #include<iostream> # ...

  6. 问题-关于sharemem程序访问WEB出现内存错误处理

    [delphi技术] 关于sharemem造成dll错误的处理办法问题现象:如果程序和dll之间用string作为参数传递时容易出现错误问题处理:需要在程序的uses中使用sharemem.这个sha ...

  7. 问题-Delphi不能进行调试模式

    问题现象:delphi不能进行调试模式 问题原因:可能是防火墙的原因,我有一次在电脑清理时,把delphi的调试程序名放在了360的黑名单中,按F9后,无法调试. 问题处理:你懂的(要不白名单,要不你 ...

  8. STM32F103 与 STM32F407引脚兼容问题

    ===========突袭网收集的解决方案如下=========== 解决方案1: STM32F103有的功能407都有,并且这些功能的引脚完全兼容,只是程序不同而已...而STM32F407有的功能 ...

  9. JavaScript----this陷阱的最全收集

    原文翻译: JavaScript来自一门健全的语言,所以你可能觉得JavaScript中的this和其他面向对象的语言如java的this一样,是指存储在实例属性中的值.事实并非如此,在JavaScr ...

  10. 如何调试什么时候SaveFileDialog会被Dispose

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:如何调试什么时候SaveFileDialog会被Dispose.