سؤال

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

هل كانت مفيدة؟

المحلول

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;
}

نصائح أخرى

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;   
}

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);
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى magento.stackexchange
scroll top