2

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.

rennat
  • 2,529
  • 3
  • 26
  • 30

3 Answers3

9

(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.

i.e. 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 (i.e. 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'));
Alana Storm
  • 164,128
  • 91
  • 395
  • 599
  • See the answer below, the above might work work within the flow of one page checkout's ajax system. – Alana Storm Jan 19 '11 at 23:12
  • For anyone copy/pasting - `Mage::app()->getResponse()->setRedirct(Mage::getUrl('checkout/cart'));` should be `Mage::app()->getResponse()->setRedirect(Mage::getUrl('checkout/cart'));` I would edit but it's less than 6 characters. Thank you Alan! – nick.graziano Apr 05 '16 at 23:07
9

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);
}
clockworkgeek
  • 37,650
  • 9
  • 89
  • 127
  • Ah, I'd misread which event the OP was hooking into. I thought it was the final submission. My instincts say there's still a way to do this sans override, although an override may be the best path forward if you're not redistributing the code. – Alana Storm Jan 19 '11 at 01:38
  • sadly, I am redistributing the code :( it's a module for a shipping provider to give to clients – rennat Jan 19 '11 at 21:50
  • 1
    @rennat: That's not what I meant by redistribution. You're giving this to clients. That means you have an existing relationship with them, and on the slim chance they have another module which is also trying to override your function, you can help them work through that. Also, just because **I**'ve come to conslutions that class overrides shouldn't be used in redistributable code certainly hasn't stopped extension developers from doing so. – Alana Storm Jan 19 '11 at 23:15
3

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.

dan.codes
  • 3,523
  • 9
  • 45
  • 59
  • I have used a similar approach before, although I used the Mage::register()/Mage::registry() methods rather than setting in the session. But you really have to be careful with this way of doing things. The main problem is that the regular methods are modifying data in the session as well. For example this snippet is in the saveShippingMethodAction()... $this->getCheckout() ->setStepData('shipping_method', 'complete', true) ->setStepData('payment', 'allow', true); – shaune Mar 12 '11 at 02:03