In an Observer method, how do you tell Magento to not process code after the dispatched event

dan.codes picture dan.codes · Mar 13, 2011 · Viewed 10.7k times · Source

I have some code in the checkout where I set a key in the session if that key is set to false anywhere in the checkout I need to send them back to the billing page. I have the code for it, but I also can't have any of the code that is typically ran after the observer because it will call a third party service and come back as wrong because of this key that is missing in the session

Here is my code, I have everything I want but i need the response to happen immediatly and for nothing after the dispatched event line to be fired only the response sent back to the browser.

public function checkForOrdKey(Varien_Event_Observer $observer)
    {
        $controllerAction = $observer->getControllerAction();
        $request = $controllerAction->getRequest();
        $controllerName = $request->getControllerName();
        $stepData = $this->_getCheckoutSession()->getStepData();
        $ordKeyRemoved = $this->_getCheckoutSession()->getOrdKeyRemoved();
        // if it is the checkout onepage controller or inventory controller don't do anything
        if (isset($controllerName) && $controllerName === "onepage" && $stepData['shipping']['complete'] && $ordKeyRemoved) {
            $this->_getCheckoutSession()->setStepData('shipping', 'complete', false);
            $result['goto_section'] = 'billing';
            Mage::app()->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
            $this->_getCheckoutSession()->setOrdKeyRemoved(false);

        }
    }

Answer

Jonathan Day picture Jonathan Day · Mar 13, 2011

Basically you need to take control of the creation and sending of the Response object. The normal flow of the controller will process all the method's inline logic, fire it's Events and collect additions to the Response along the way, then the Magento framework will finalize and send the Response.

You can short-circuit that flow in the Observer by attaching to the preDispatch event (controller_action_predispatch_checkout_onepage_savebilling) and then executing this:

$request = Mage::app()->getRequest();
$action = $request->getActionName();
Mage::app()->getFrontController()->getAction()->setFlag($action, Mage_Core_Controller_Varien_Action::FLAG_NO_DISPATCH, true);

The lines above instruct Mage_Core_Controller_Varien_Action (grandparent of all controllers) to bypass the action that has been called (review line 414 in CE 1.4.2 to see how this works). Then proceed with creating your own response and sending it back to the browser. You will need to investigate the correct JSON format to have to the checkout JS classes render any error messages, but something along these lines...

$response = Mage::app()->getResponse();
$response->setHttpResponseCode(500);  //adjust to be whatever code is relevant
$json = Mage::helper('core')->jsonEncode($this->__('Your message here'));  //adjust
$response->setBody($json);
//don't need to sendResponse() as the framework will do this later

That way you're working within the Zend/Magento framework and you don't need to Override the CheckoutController (please, never ever...) or use "exit/die()" hackiness. The reason that exit/die is bad is that it prevents any later Observers that have registered an interest in that Event being able to act. It would be extremely frustrating as a developer to register an Observer that never gets called because another developer has exit'd before you get hit!!

Note that setting the no-dispatch flag will only work if you are hooked into the predispatch Event.

For further info, review the Magento sequence diagram to see how you are bypassing the Layout/Block/Template sections of the flow.