Did you know that instead of making use of rewrite-rules, you can use Magento its event driven architecture to customize the backend? It’s a lot cleaner and arguably easier, too.

Magento Core Rewrites

With old-fashioned rewrites not only do you become a dependency, you are also possibly breaking code which depends on core functionality if you do not exactly imitate the original behavior in your rewrite. To understand the problem, consider a client which wants a button added to the order overview (Sales > Order > View) which ships that order its details to some third party order tracker.

You do some quick research and quickly find out that buttons are added to the order view screen in Mage_Adminhtml_Block_Sales_Order_View its constructor:

// [...] $this->_addButton('order_edit', array( 'label' => Mage::helper('sales')->__('Edit'), 'onclick' => $onclickJs )); // [...] 1 2 3 4 5 6 // [...] $ this -> _addButton ( 'order_edit' , array ( 'label' = > Mage :: helper ( 'sales' ) -> __ ( 'Edit' ) , 'onclick' = > $ onclickJs ) ) ; // [...]

Now we have identified how it’s done, we can simply add a rewrite rule to our config.xml for that very class, extend it, inherit the constructor and add a function call to create the desired custom button with a click event redirecting to our controller. It works, right? Depends. What if an already installed extension with higher priority already rewrote the Order_View block?

Use Events Instead

Nothing will happen because you are rewriting something which isn’t even used anymore in the first place! This is why you should use events. And in this case, the adminhtml_block_html_before event declared in Mage_Adminhtml_Block_Template its __toHtml method. Because all Adminhtml blocks extend that Template class and the __toHtml method is used to render the HTML of the Block in question, you have a guarantee that the event will be be fired just before the Block is rendered.

We can use this in our advantage by simply hooking an observer to the adminhtml_block_html_before event in the config.xml file:

<events> <adminhtml_block_html_before> <observers> <domain_module_add_custom_button> <class>Domain_Module_Model_AdminhtmlBlockObserver</class> <method>addCustomButtonToOrderView</method> </domain_module_add_custom_button> </observers> </adminhtml_block_html_before> </events> 1 2 3 4 5 6 7 8 9 10 < events > < adminhtml_block_html_before > < observers > < domain_module_add_custom_button > < class > Domain_Module_Model_AdminhtmlBlockObserver < / class > < method > addCustomButtonToOrderView < / method > < / domain_module_add_custom_button > < / observers > < / adminhtml_block_html_before > < / events >

In the observer itself, we only have to check whether we are dealing with our target block, all other blocks will be left untouched. Note that we are also checking whether the block in question is a subclass of our target block in case a module with a higher priority has already rewritten and extended our target class.

<?php class Domain_Module_Model_AdminhtmlBlockObserver { const TARGET_CLASS = 'Mage_Adminhtml_Block_Sales_Order_View'; public function addCustomButtonToOrderView(Varien_Event_Observer $observer) { $block = $observer->getEvent()->getBlock(); // Skip anything which isn't our target if (! (get_class($block) === self::TARGET_CLASS || is_subclass_of($block, self::TARGET_CLASS))) { return; } $block->addButton( 'some_id', array( 'label' => Mage::helper('module')->__('Custom button'), 'onclick' => 'setLocation(\'somewhere\');', 'class' => 'go' ) ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 < ? php class Domain_Module_Model_AdminhtmlBlockObserver { const TARGET_CLASS = 'Mage_Adminhtml_Block_Sales_Order_View' ; public function addCustomButtonToOrderView ( Varien_Event _ Observer $ observer ) { $ block = $ observer -> getEvent ( ) -> getBlock ( ) ; // Skip anything which isn't our target if ( ! ( get_class ( $ block ) === self :: TARGET_CLASS || is_subclass_of ( $ block , self :: TARGET_CLASS ) ) ) { return ; } $ block -> addButton ( 'some_id' , array ( 'label' = > Mage :: helper ( 'module' ) -> __ ( 'Custom button' ) , 'onclick' = > 'setLocation(\'somewhere\');' , 'class' = > 'go' ) ) ; } }

And with that, we have a pretty fail safe yet clean method to customize the Magento backend which doesn’t clash with other extensions.