Categories
Magento 1

Simplified Magento Flow with Events

The Mage_Core_Controller_Varien_Front is instantiated. This is the class which is always used despite its front name. It picks which routers should be used.

Event: controller_front_init_before
Event: controller_front_init_routers

Mage_Core_Controller_Varien_Front → dispatch() → Router Match Loop. The standard routers are:

  • Mage_Core_Controller_Varien_Router_Standard (Frontend)
  • Mage_Core_Controller_Varien_Router_Admin (Admin)
  • Mage_Core_Controller_Varien_Router_Default (Default)

When a router matches in the loop, its job is to set the following on the request:

$request->setModuleName($module);
$request->setControllerName($controller)
$request->setActionName($action)
$request->setControllerModule($realModule)
$request->setDispatched(true);

And then calls dispatch on the instance of the controller (Eg. Mage_Cms_IndexController).

Dispatch is defined in Mage_Core_Controller_Varien_Action, which is extended by Mage_Core_Controller_Front_Action, which should be extended by custom controllers on the frontend.

PreDispatch

The dispatch method calls predispatch in the controller. To not dispatch the controller, I.e to forward to another controller at this point, set:

$request->setDispatched(false);

And the controller’s action method will not be called. To switch controller, action, etc, change the values on the request object, and the router’s match loop will continue and attempt to dispatch the new controller. The predispatch method then fires:

Event: controller_action_predispatch
Event: controller_action_predispatch_ + getRouteName()
Event: controller_action_predispatch_ + getFullActionName()

These events can also be used to hook into, and forward to another controller if required. At this point, no blocks are created – the layout cannot be manipulated. If the request’s isDispatched method, remains as true, then the flow continues, and the controller’s action method is executed.

LoadLayout

The following two methods (loadLayout and renderLayout) must be called from the custom controller’s action method for these to apply

 $controller->loadLayout($handles)

is called with the option to supply handles. These replace the ‘default’ handle, so if using this, pass default in your handles array. Note: These handles will be added before any action layout handles, so the instructions within them may be overwritten.

addActionLayoutHandles()

adds the STORE_code layout handle, theme handle (THEME_*), and the full action name in lowercase (E.g. customer_account_index)

loadLayoutUpdates()

Event: controller_action_layout_load_before
This is the best event to use to add custom layout handles, however it will fire for every controller. See this for workarounds.

generateLayoutXml()

Event: controller_action_layout_generate_xml_before

generateLayoutBlocks()

Event: controller_action_layout_generate_blocks_before
The XML has been generated at this point, not any actual layout blocks. If required, modifications to the XML could be made at this point.

$layout->generateBlocks()

The blocks are now created. Each block fires:
Event: core_block_abstract_prepare_layout_before
before the block’s _prepareLayout() is called, followed by
*Event:* core_block_abstract_prepare_layout_after

Event: controller_action_layout_generate_blocks_after
This is where blocks can programatically be inserted into the layout.

RenderLayout

Event: controller_action_layout_render_before
Event: controller_action_layout_render_before_ + fullActionName
where blocks can also be added to the layout programatically, however a reference to the layout object is not passed in the dispatchd observer, so the layout must be retrieved using Mage::app()->getLayout()

PostDispatch

The controller’s postDispatch() method is then called, firing the following events:

Event: controller_action_postdispatch
Event: controller_action_postdispatch_ + $this->getFullActionName()
Event: controller_action_postdispatch_ + $this->getRouteName()

Categories
Magento 1

Adding Layout Handles Programatically (and in order)

Adding custom layout handles in the correct order can sometimes be an awkward process, it can often be tempting to use either the controller_action_predispatch or the controller_action_predispatch_fullActionName, event and then add the layout handle in the corresponding observer code.

The problem with this approach is that these events are fired in the controller’s preDispatch method, meaning loadLayout hasn’t yet been called and there won’t be any other layout handles present at that point. This causes problems because your layout handle may rely on items from other layout handles which haven’t been loaded at that point.

Correcting the handles’ order

One method to fix this issue is to use the controller_action_layout_load_before event which is fired in the loadLayoutUpdates, method (this is called via the loadLayout method). At this point, addActionLayoutHandles has already been executed, which makes relying on other handles a possibility. One problem with this approach is the observer method will be called for every page, which won’t work correctly for code which relies on running for a fullActionName. One fix for this method is to either check the existence of a required layout handle or controller action’s full name in the observer code.

XML

<controller_action_layout_load_before>
    <observers>
        <Demo_Module>
            <class>skin/observer</class>
            <method>updateLayoutHandle</method>
        </Demo_Module>
    </observers>
</controller_action_layout_load_before>

Observer

public function updateLayoutHandle($observer)
{
    $update = $observer->getEvent()->getLayout()->getUpdate();
    $handles = $update->getHandles();

    foreach($handles as $k => $handle){
        if($handle == 'catalog_category_default'){
            $handles[$k] = 'catalog_category_layered';
        }
    }

    $update->resetHandles();
    $update->addHandle($handles);   
}

This allows us to get all layout handles, change them as necessary, and re-add them (an array can be passed to the addHandle method). We could also splice in handles at required points using this method.