简体   繁体   中英

What is the correct way to stop a checkout from an event observer in Magento?

I am verifying shipping quotes during the event checkout_controller_onepage_save_shipping_method and if the verification fails i want to send the user back to shipping method selection but I would also like to display a message saying why it failed. Does Magento have a way of doing this built in?

I am already verifying the data I just lack the redirect to shipping methods and way to display a message.

(None of this is tested code, but the concepts should get you where you need to go)

Magento is a project run by a bunch of software engineers. When you're working with a bunch of software engineers, the documentation is the code.

ie Whenever you need to do something common with Magento, observe how the core team has done it, taking into account that you should limit yourself to observers, overrides, and new code since you can't discuss your changes with said core team.

Take a look at the one page checkout controller's IndexAction method

app/code/core/Mage/Checkout/controllers/OnepageController.php
public function indexAction()
{
    if (!Mage::helper('checkout')->canOnepageCheckout()) {
        Mage::getSingleton('checkout/session')->addError($this->__('The onepage checkout is disabled.'));
        $this->_redirect('checkout/cart');
        return;
    }
    ... 

Magento allows you add errors to the session object, which will be processed by the messaging block on the next request.

Mage::getSingleton('checkout/session')->addError($this->__('The onepage checkout is disabled.'));

That that handles an error. Next, there's the redirect. This happens here

$this->_redirect('checkout/cart');

Since you're calling this code from an observer, you won't have access to this method. However, if you examine the controller

/**
 * Retrieve request object
 *
 * @return Mage_Core_Controller_Request_Http
 */
public function getRequest()
{
    return $this->_request;
}
...
protected function _redirect($path, $arguments=array())
{
    $this->getResponse()->setRedirect(Mage::getUrl($path, $arguments));
    return $this;
}

You can see its using the response object. Magento uses a global response object (similar to Zend, and other Web frameworks) to handle what gets sent back to the browser (ie redirect headers). You can get a reference to the same object via

Mage::app()->getResponse()

and could perform a redirect with something like

Mage::app()->getResponse()->setRedirect(Mage::getUrl('checkout/cart'));

Alan Storm's answer is, as ever, informative and enlightening. But in this situation the onepage checkout is mostly AJAX which ignores the session error message, you won't see it until leaving the checkout page.

In saveShippingMethodAction there is the following line:

$result = $this->getOnepage()->saveShippingMethod($data);

...and then $result is JSON encoded. If you override Mage_Checkout_Model_Type_Onepage::saveShippingMethod to perform your check then control what is returned you can insert an error message that will be returned to the browser and shown to the user in a popup.

Your override might look something like this:

public function saveShippingMethod($shippingMethod)
{
    if ($this->doesntApplyHere()) {
        return array('error' => -1, 'message' => $this->_helper->__('Explain the problem here.'));
    }
    return parent::saveShippingMethod($shippingMethod);
}

I came up with a different approach for this, not having to override the controller. Basically I do the same thing but just in observer methods. So I am using checkout_controller_onepage_save_shipping_method to validate the shipping methods as well and if there is an error I add that error to a session variable like the following

 $error = array('error' => -1, 'message' => Mage::helper('core')->__("Message here"));
 Mage::getSingleton('checkout/session')->setSavedMethodError($error);

Then you can for every action apply another observer to 'controller_action_postdispatch_'.$this->getFullActionName()

So I used that to observe controller_action_postdispatch_checkout_onepage_saveShippingMethod Which in there I check for the session error variable and set the response body if it exists.

$error =  Mage::getSingleton('checkout/session')->getSavedMethodError();
if($error){
   Mage::app()->getResponse()->setBody(Mage::helper('core')->jsonEncode($error));
}
Mage::getSingleton('checkout/session')->setSavedMethodError(false);

I am not sure if this is better or worse, so please leave any comments but I know I preferred to be able to do this without having to do any rewriting of the class.

This works because you are overriding the response body that was set in the saveShippingMethod action.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM