3

I'm trying to create a minimum total checkout module that will prevent someone from checking out with a total less than a configurable amount.

I'm using the event sales_quote_save_before to display an error on the checkout/cart page when it's opened.

<?xml version="1.0"?>
<config>
    <frontend>
        <events>
            <sales_quote_save_before>
                <observers>
                    <b2b>
                        <class>b2b/observer</class>
                        <method>checkTotalsCart</method>
                    </b2b>
                </observers>
            </sales_quote_save_before>
        </events>
    </frontend>
</config>

And in the observer

public function checkTotalsCart()
{
    if ($this->_hasCartError()) { /* does some checks, returns bool */
        $this->_setErrorMessage();
    }
}
protected function _setErrorMessage()
{
    $session = Mage::getSingleton("b2b/session"); /* extends Mage_Core_Model_Session */
    $session->addError($this->helper->getErrorMessage());
}

The problem is that when you update the cart from the cart page, the error message is showing up twice. I guess that event is happening multiple times.

enter image description here

I've tried to check if the message was previously set with a custom session variable

protected function _setErrorMessage()
{
    $session = Mage::getSingleton("b2b/session");
    if ($session->isErrorMessageAdded()) {
        return;
    }
    $session->addError($this->helper->getErrorMessage());
    $session->isErrorMessageAdded(true);
}

But that didn't work either. How can I make sure an error message is only showing up once per page request?


Relevant Module files

More

9

Your thinking was sound, but your understanding of Magento's magic methods is a little shaky. The hasX isn't a setter, it only checks for the presence of a data property. So you'd want to change

$session->hasErrorMessage(true);

to

$session->setErrorMessage(true);

Also, if b2b/session is an actual session object (i.e. it persists things to the PHP session for later retrieval) I'd be wary of using it for something like this. If you accidentally persist the 'error_message` property to the session that means the user will only ever see this error message once per session, when (it appears) your desire is to have them see it once per invalid order attempts.

I usually prefer a static PHP class variable for something like this

static protected $_hasErrorMessage=false;
protected function _setErrorMessage()
{
    $session = Mage::getSingleton("b2b/session");
    if (self::$_hasErrorMessage) {
        return;
    }
    $session->addError($this->helper->getErrorMessage());
    self::$_hasErrorMessage = true;
}
  • Yeah I like this better than my clear approach. Nicely done. – philwinkle Aug 7 '13 at 17:52
  • Yes, I had tried this approach before but it still isn't working. That's when I escalated it to session storage instead (and I have another observer on core_session_abstract_clear_messages that sets it back to null so it will work for the next page request). Also, hasErrorMessage() isn't dependent on a magic __call() btw, it's a custom getter/setter depending on if an arg is passed. – Steve Robbins Aug 7 '13 at 18:10
  • @SteveRobbins That static property message should work — if it doesn't that means 1. Your error is being added from somewhere else, 2. There's ajax requests going on that are adding multiple errors during multiple requests. 3. You're inadvertently adding errors to different session objects. Also — you may want to skip setting things this high in the chain, and concentrate on only display a single error message at that layout/view level. Good luck! – Alan Storm Aug 7 '13 at 18:16
  • @SteveRobbins Also, based on your updated post, I don't see any hasErrorMessage defined in your session object. – Alan Storm Aug 7 '13 at 18:23
  • It got renamed so it wouldn't confused with the magics. Also, it looks like it's #2 or 3 of your suggestion. It happens once when you click the "Update Cart" and get's set to checkout/cart/updatePost controller, then again when it refreshes the page/redirects you. I'm not sure why that isn't being caught in the session though. – Steve Robbins Aug 7 '13 at 18:27
3

Let's explore addError a moment. addError is a convenience method for not having to make error message calls like so:

Mage::getModel('core/message_collection')->add(Mage::getSingleton('core/message')->error($message));

As you can see that's really messy.

Most of the implementations of addError, including that in Mage_Core_Model_Session_Abstract, have a method called addMessage, which essentially do what is listed above. It uses a local method called getMessages which implements the message collection pattern already described. See below:

public function getMessages($clear=false)
{
    //....stuff
    if ($clear) {
        $messages = clone $this->getData('messages');
        $this->getData('messages')->clear();
        Mage::dispatchEvent('core_session_abstract_clear_messages');
        return $messages;
    }
    //...stuff
}

So how does this help you?

You could provide the addMessage method in your own b2b/session model to conditionally pass true to getMessages to clear it out, effectively displaying only the last message set:

public function addMessage(Mage_Core_Model_Message_Abstract $message, $clear=false)
{

    $this->getMessages($clear)->add($message);
    Mage::dispatchEvent('core_session_abstract_add_message');
    return $this;   
}
  • Suppose someone adds a product to their cart. Would this also remove the "Product was added to cart" message and then only display my error message? I would want both to show – Steve Robbins Aug 7 '13 at 18:07
  • Wonderful point - I'm not sure! You're probably right and that's why I prefer Alan's method – philwinkle Aug 7 '13 at 19:18
1

One solution I've used (thought it isn't too pretty) is to loop through the messages for the session and see if that string already exists

/**
 * @var Mage_Checkout_Model_Session $_checkout
 */
protected $_checkout;
protected function _addMessage($message)
{
    $messages = array_values((array)$this->_checkout->getMessages());

    foreach ($messages[0] as $existingMessages) {
        foreach($existingMessages as $existingMessage) {
            $existingMessage = array_values((array)$existingMessage);

            if ($existingMessage[1] == $message) {
                // If the message is already set, stop here
                return;
            }
        }
    }

    // Add this message to the session/checkout
    $this->_checkout->addError($message);
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.