Understanding layout.xml syntax in Magento

I write this article about my understanding of layout.xml up to now. I use it to show you

  • How to add buttons in order/invoice/shipment/creditmemo without override Block class?
  • How to define an array as a parameter in layout.xml using <action method=”addButton”><param>array</param></action>?
  • How to assign any type of variables in layout.xml?
  • How to avoid overriding controller class but still get the result you want on a specific url?

as well.

All my understanding was inspired by the task: adding buttons in order view at backend to offer administrators extra order process facilities.

add-buttons-to-backend-order-view

I love Magento class overriding architecture, but as I have learned the downside of overriding (Basically, there is only one chance of overriding for each class among all extension classes. Therefore, the more classes overridden, the greater chance of conflict between extensions.), I create a principle for my programming in Magento – do not override Magento class unless there is no other way around it.

So, back to the task. It would be very easy to add the button if I had overridden Mage_Adminhtml_Block_Sales_Order_View. It could be

public function __construct() {
parent::__construct();
$this->_addButton('new_button', array(
'label'    => Mage::helper('sales')->__('New Button'),
'onclick'  => "window.open('{$this->getUrlForNewButton()}')",
))
;
}

However, I do not want to override Mage_Adminhtml_Block_Sales_Order_View. How about firing addButton method in layout.xml? I see an example of addTab at the left side

<block type="adminhtml/sales_order_view_tabs" name="sales_order_tabs">
<action method="addTab"><name>order_info</name><block>order_tab_info</block></action>
<action method="addTab"><name>order_invoices</name><block>adminhtml/sales_order_view_tab_invoices</block></action>
<action method="addTab"><name>order_creditmemos</name><block>adminhtml/sales_order_view_tab_creditmemos</block></action>
<action method="addTab"><name>order_shipments</name><block>adminhtml/sales_order_view_tab_shipments</block></action>
<action method="addTab"><name>order_history</name><block>adminhtml/sales_order_view_tab_history</block></action>
</block>

In the example, addTab method takes two parameters, one for tab name and the other for block name. Inside addTab method, it creates a block object from the block name, which is passed in as a string. In fact, the tag name <name> or <block> is not significant in layout.xml. It does not represent parameter name in method. But the position is significant – the first tag is passed into method as the first parameter, and ditto the followings.

Back to addButton method in Mage_Adminhtml_Block_Widget_Container. It takes at least two parameters, one as string and the second as associated array. So the layout.xml can be something like this:


<reference name="sales_order_edit">
<action method="addButton" translate="label">
<name>new_button</name>
<button_array><!-- this tag name is not significant -->
<label>New Button</label>
<onclick>window.open('{$this->getUrlForNewButton()}')</onclick>
</button_array>
</action>
</reference>

The difficult part is in <onclick> – it requires a variable of current order id. After some digging, I find another example:


<reference name="footer_links">
<action method="addLink" translate="label title" module="catalog" ifconfig="catalog/seo/site_map"><label>Site Map</label><url helper="catalog/map/getCategoryUrl" /><title>Site Map</title></action>
</reference>

So, I create a helper method to return url for new button and modify layout.xml to


<reference name="sales_order_edit">
<action method="addButton" translate="label">
<name>new_button</name>
<button_array><!-- this tag name is not significant -->
<label>New Button</label>
<onclick helper="mymodule/getUrlForNewButton"/>
</button_array>
</action>
</reference>

Unfortunately, it did not work. I assume the helper callback does not work if it is in the array. So I change the helper method to return an associated array directly as the second parameter for layout.xml and the final layout.xml is


<reference name="sales_order_edit">
<action method="addButton" translate="label">
<name>new_button</name>
<button helper="mymodule/getNewButtonSecondParam"/>
</action>
</reference>

It works perfectly. I am very proud of myself.

Some while ago, I overrode Mage_Catalog_ProductController just to register a variable in viewAction


public function viewAction() {
Mage::register('my_var', $value);
parent::viewAction();
}

Now I reviewing that approach stupid. I could register a variable using layout.xml without any class overriding!

7 comments

  1. 这个方法在1.4上还能用吗?我加button之后会抱错

    Notice: Undefined index: label in /home/oshpcntr/public_html/app/code/core/Mage/Core/Model/Layout.php on line 370

  2. 不知道我漏了什么东西,能给我一个完整的文件参考一下吗?谢谢

  3. This is great. Thanks for sharing. There is so much stuff going on with layout xml and it is so difficult to figure it out without documentation. You have done all of us a very important service by sharing!

Leave a comment

Your email address will not be published. Required fields are marked *