我們在這個部分關注一下View裡Layouts和Blocks。 跟其他主流PHPMVC架構不一樣,magento 的ActionController不會把數據對象傳給view,也不會設置View對象裡的屬性。View是通過系統模塊去獲取它所需要的信息。 這個設計的結果是View被分為Blocks 和Templates。Blocks是PHP對象,Templates是PHP代碼和HTML的混合(也可以認為是PHP作為了模版語言)。每個Block綁定到一個Template文件。在一個Phtml文件裡,PHP的關鍵字$this會包含了對Temeplate對應Block的引用。 下面是一個快速的例子。查看模版文件app/design/frontend/base/default/template/catalog/product/list.phtml 會看到如下的代碼 <?php$_productCollection=$this->getLoadedProductCollection() ?> <?phpif(!$_productCollection->count()): ?> <divclass="note-msg"> <?php echo$this->__("There are no products matching the selection.")?> </div> <?php else: ?> 其中的getLoadedProudctController可以在對應的block文件找到 app/code/core/Mage/Catalog/Block/Product/List.php public functiongetLoadedProductCollection() { return$this->_getProductCollection(); } 其中的_getProductCollection會實例化models,並取到數據給對應template。 內嵌Block Blocks/Templates真正的強大的地方是getChildHtml方法。這可以讓我們包含次一級的Block/Template在主的Block/Template裡面(xml的格式) Blocks調用Blocks是會組成我們整個HTMLlayout。看一個例子 App/design/frotend/base/default/template/page/1column.phtml <!DOCTYPE htmlPUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <htmlxmlns="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 class="page-popup <?php echo$this->getBodyClass()?$this->getBodyClass():'' ?>"> <?php echo$this->getChildHtml('content') ?> <?php echo $this->getChildHtml('before_body_end') ?> <?php echo$this->getAbsoluteFooter() ?> </body> </html> 該文件不長,但是每個調用都是$this->getChildHtml(…),這會包含並渲染其他的block。這些block也可能會調用其他block 。 Layout 盡管Block和Template很好,但是你可能會有下面的疑問 1. 我怎麼告訴Magento哪個Block在頁面中使用? 2. 我們怎麼告訴Magento是初始的 3. 我怎麼告訴每個Block去調用下面的block 這時候就需要Layout對象了。Layout對象是Xml格式,定義一個頁面裡包含哪些Blocks,並且哪些Blocks負責渲染頁面。 之前的Hello World項目我們直接在Action Method上做的。這次我們創建一個簡單的HTMLtemplate來為該模塊服務。 首先創建一個文件 App/design/frontend/base/default/layout/local.xml 然後寫入下面的內容 <layoutversion="0.1.0"> <default> <reference name="root"> <block type="page/html"name="root" output="toHtml"template="simple_page.phtml" /> </reference> </default> </layout> 然後再創建一個文件 app/design/frontend/base/default/template/simple_page.phtml(注意和配置裡的template一致) 寫入以下內容 <!DOCTYPE html PUBLIC"-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <htmlxmlns="http://www.w3.org/1999/xhtml"> <head> <title>Untitled</title> <metaname="generator" content="BBEdit 9.2" /> <styletype="text/css"> body { background-color:#f00; } </style> </head> <body> </body> </html> 最後在Aciton Controller裡負責啟動layout過程。增加下面兩行代碼 public functionindexAction() { //remove our previous echo //echo'Hello Index!'; $this->loadLayout(); $this->renderLayout(); } 清除緩存,重新加載Hello World controller頁面,可以看到頁面的背景hi紅色,並且Html源碼和simple_page.phtml也是對應的。 發生了什麼? 剛才的一切看上去很神秘。我們來仔細的看看這個過程。首先,我們想安裝Layoutviewer模塊。這個模塊和Configviewer很像。 一旦裝好了,就可以用下面的URL http://localhost/magento/helloworld/index/index?showLayout=page 這個是一個layout xml對應於請求的頁面。由<block /> <refenece /> 和 <remove />標簽組成。當你調用loadLaout方法時, 1. 生成一個Layout xml 2. 實例化<block/> 和<reference />下面的Block類。查找用標簽name屬性的,在全局配置文件裡找到對應的,並儲存在Layout對象的internal_blocks數組裡。 3. 如果<block />標簽包含了輸出屬性,它的值也加入到Layout對象的internal_blocks數組裡。 這樣一來,當我們在Action Controller裡調用renderLayout時,Mageno 會迭代_blocks數組裡所有的Blocks, 並把它對應的輸出屬性作為回調函數。這相當於是向Html的轉化,意味著Block的Template就是輸出的起點。 下面的部分涉及到Block如何實例化,Layout文件如何生成,還有output的結束。 Block實例化 在LayoutXml裡,<block/>或者<reference/>有個類型相當於是URI <block type=”page/html” … <block type=”page/template_links” URI會在全局配置文件指明一個地址。URI的第一個部分用來去查找全局配置文件,找到Page類名。第二部分跟在第一個部分後面成為新的類, 然後再實例化該類。 以page/html作為例子。首先Magento會在全局配置文件裡查找下面的 /global/blocks/page 然後找到 <page> <class> Mage_Page_Block </class> </page> 這樣我們就得到了MagePageBlock類。然後,URI的第二部分會加在它後面成為MagePageBlock_Html。這個類隨後就會呗實例化。 Blocks也是Magento裡的組類,所有的共享類似的實例化方法。後面會有該部分的詳細介紹。 <block />和<reference />之間的不同 我們談到了<blocks/>和<references />都可以實例化Block,那他們有什麼不同呢。 先有 <blocktype="page/html" name="root" output="toHtml"template="page/2columns-left.phtml"> <!-- ... sub blocks ... --></block> 然後有 <layoutversion="0.1.0"> <default> <reference name="root"> <block type="page/html"name="root" output="toHtml"template="simple_page.phtml" /> </reference> </default> </layout> <reference />裡面的blocks不會替代blocks。相反,他們是增加,或者修改現有的blocks。上面的樣例中,是插入了一個新的叫root的block到現有的rootblock中。這在Magento Layout中是未定義的。最終結果是老的被替換掉了,但是靠此來保證一致性是很糟的主意。 Layout 文件如何生成 到現在我們對Layout XML應該有比較清晰的認識了。但是Layout XML從何而來?要解答這個問題,我們需要引入兩個新的概念,Handles和Package Layout。 Handles Magento中的每個請求會生成幾個不同的Handles。Layoutview 模塊就可以給我們用URL展示這些 http://localhost/magento/helloworld/index/index?showLayout=handles 我們會看到類似 1. 默認 2. STORE_bare_us 3. THEME_frontend_default_default 4. Helloworld_index_index 5. Customer_logged_out 這些每個都是一個Handle。Handle在Magento的很多地方會被設置。我們需要關注其中兩個地方:default和helloworld_index_index.。默認的Handle是每個請求都會出現的。而Helloworld_index_indexHandle是通過合並frontName(helloworld),Actioncontroller (index), 和Action Controller Action Method(index)而成。這意味這每個ActionController方法都可能對應一個Handle。 記住“index”是Magento對每個Action Controller和ActionMethods的默認,因此下面的請求 http://localhost/magento/helloworld/?showLayout=handles 同樣會產生Handle的名字交helloworld_index_index Package Layout 你可以認為PackageLayout等同於全局配置。他是一個大的XML文件,包含了Magento內每個可能的Layout配置。讓我們看一下 http://localhost/magento/helloworld/index/index?showLayout=package 這個可能會加載一會。如果浏覽器在xml下卡了,請換成text模式 http://localhost/magento/helloworld/index/index?showLayout=package&showLayoutFormat=text 你可以看到很大的XML文件。這就是Package Layout。它是綜合了所有當前主題下XMLLayout的文件。默認的安裝是 app/design/frontend/base/default/layout/ 在全局文件裡面有個<updates />部分,節點中包含了所有要加載的名字。一旦配置文件中給出的文件合並了,Magento會合並到上一個xml文件,local.xml。這個可增加你想要的功能。 合並Hanldes和Packge Layout 如果你看到Package Layout, 你可以看到一些熟悉的標簽,例如<block />和<reference/>, 但是他們都類似這樣的標簽覆蓋 <default /> <catelogsearch_advanced_index/> etc… 這些都是Handle標簽。一個請求的Layout是由所有匹配請求的Handles的Package Layout生成。因此,在上面的例子中,我們的layout是在下面的部分中生成 <default /> <STORE_bare_us /> <THEME_frontend_default_default/> <helloworld_index_index/> <customer_logged_out/> 還有一個標簽需要我們注意。<update />容許我們包含其他的Handle。例如 <customer_account_index> <!-- ... --> <update handle="customer_account"/> <!-- ... --> </customer_account_index> 這意味這請求到customeraccountindex時,應該包含<customer_account>下面的<reference/>和<block />. 學以致用 看夠了理論,我們來回顧一下之前的工作。 <layoutversion="0.1.0"> <default> <referencename="root"> <blocktype="page/html" name="root" output="toHtml"template="simple_page.phtml" /> </reference> </default> </layout> 這個意味著我們重寫了root標簽。而<default/>部分保證了每次請求都會發生。這可能並不是我們想要的效果。 如果訪問任意其他頁面,我們同樣是空白頁面,或者是紅色背景(之前helloworl頁面的那樣)。所以我們改進一下local.xml,確保它只用於helloworld頁面。修改如下 <layout version="0.1.0"> <helloworld_index_index> <referencename="root"> <blocktype="page/html" name="root" output="toHtml"template="simple_page.phtml" /> </reference> </helloworld_index_index> </layout> 清除cache,這個時候你的其他頁面應該恢復了。 然後應用到googbye Aciton Method public function goodbyeAction() { $this->loadLayout(); $this->renderLayout(); } 這個時候加載http://localhost/magento/helloworld/index/goodbye 會發現還是blank頁面。這個時候我們需要在local.xml增加一個actionname, 內容如下 <layout version="0.1.0"> <!-- ... --> <helloworld_index_goodbye> <updatehandle="helloworld_index_index" /> </helloworld_index_goodbye> </layout> 清楚cache,這個時候,下面兩個頁面會有同樣的效果了。 http://localhost/magento/helloworld/index/index http://localhost/magento/helloworld/index/goodbye 開始輸出並getChildHtml 在標准的配置中,輸出的開始是root命名的Block(這個是輸出的特性)。我們已經重寫了root的模版 template=”simple_page.phtml” 模版會從當前或者base主題的主目錄裡得到,如 app/design/frontend/base/default/template 通常你可以添加模版到你自己的主題或者默認主題 app/design/frontend/default/default/template app/design/frontend/default/custom/template base目錄是最後才會去查找的目錄,如果magento在其他主題下找不到,才會回到base目錄。可是,象之前提到的哪有,你不想加入到這樣的目錄,因為magento的更新會覆蓋他們。 增加內容block 紅色的悲劇很無聊。所以我們在頁面上增加點內容。改變local.xml裡的<helloworldindexindex />,如下 <helloworld_index_index> <reference name="root"> <block type="page/html"name="root" template="simple_page.phtml"> <blocktype="customer/form_register" name="customer_form_register"template="customer/form/register.phtml"/> </block> </reference> </helloworld_index_index> 我在root裡增加兩個內嵌的Block。Magento會分配它,並且展示一個客戶注冊的頁面。在root裡內嵌這個Block,我們要在simple_page.html裡面顯式的調用。所以我們用Block的getChildHtml方法,如下 <body> <?php echo $this->getChildHtml(‘customr_form_register’); ?> </body> 清除Cache,重新加載頁面。這個時候我們看到了注冊頁面在紅色的背景上。下面還有一個Block叫top.links。添加如下 <body> <h1>Links</ht> <?php echo $this->getChildHtml(‘top.links’); ?> </body> 當我們重新加載頁面,就看到Links被渲染了,但是top.links沒有任何渲染。這是因為我們沒有在local.xml裡添加它。在Layout裡,getChildHtml只能包含顯示的作為子block的Blocks。這容許Magento實例化它想要的blocks,同時讓我們可以根據顯示內容為Block設置不同的模版 我們可以在local.xml裡為top.links增加Block <helloworld_index_index> <reference name="root"> <block type="page/html" name="root"template="simple_page.phtml"> <blocktype="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> 此時再清除cache,就可以看到top.links模塊的效果了