Advanced global messages manipulation in Magento

From time to time, we have to manipulate with the messages in Magento. It is really easy if you just change the message text – in this case, you can simply open a corresponding translation file in the current package “locale” directory or “app/locale” with your favorite text editor and change an appropriate message without editing any template files.

As you can see, this is too easy to spend more than few words on it. The main goal of this article is manipulating WHERE the messages should appear.

Usually, the messages are stored in the session singleton and can be extracted from there with or without clearing the session and using:

Mage::getSingleton('tag/session')->getMessages($param)

$param – it is a boolean value which will clear the selected session messages after extraction if set as TRUE. You can get any session messages in any block (or it’s template) which extends Mage_Core_Block_Abstract (this means any properly set block) by using the raw construction like this:

$this->getLayout()->getMessagesBlock()->setMessages(Mage::getSingleton('tag/session')->getMessages(true));

 

Mage::getSingleton('core/session')->addSuccess('String');
Mage::getSingleton('core/session')->addException('String');
Mage::getSingleton('core/session')->addNotice('String');
Mage::getSingleton('core/session')->addError('String');

Moreover, the core messages output is performed by Mage_Core_Block_Messages which is usually called in “page.xml” layout:

<block type="core/messages" name="global_messages" as="global_messages"/>

The task, in our case, is to add the messages about the newsletter subscription to the newsletter block. Despite the fact that this change does not require Magento theme development, it sounds pretty naturally, we guess.

Let’s say the block was located in the footer, which is also pretty common practice – so, why should the subscribers see the messages of their subscription success (or failure) at the top of the page?
On the other hand, we can not move the whole messages block to the footer because it will contain the other messages: login failure e.g. Furthermore, the subscription and other messages can appear on the same page and at the same time, so we should think about separating different core messages types.

First of all, we need to override Mage_Newsletter_SubscriberController. So, let’s make a separate extension for this purpose and use it to reach our goal. We hope that you already know how to do that, and we will proceed to the controller overriding itself.

Let’s add a route rewrite to our extension config.xml:

<frontend>
	<routers>
		<newsletter>
			<args>
				<modules>
					<extension_tag before="Mage_Newsletter">Company_Extension_Newsletter</extension_tag>
				</modules>
			</args>
		</newsletter>
	</routers>
</frontend>

Replace a newsletter handler with any you want to override and Mage_Newsletter module with an appropriate to the chosen handler module name. Note, that the controller overriding differs from other Magento components, and we are overriding the whole route instead of a specific class. After it’s done, we can create our own controller in project/app/code/local/Company/Extension/controllers/Newsletter/SubscriberController.php:

<?php

require_once(Mage::getModuleDir('controllers','Mage_Newsletter').DS.'SubscriberController.php');

class Company_Extension_Newsletter_SubscriberController extends Mage_Newsletter_SubscriberController
{
    /**
      * New subscription action
      */
    public function newAction()
    {
        if ($this->getRequest()->isPost() && $this->getRequest()->getPost('email')) {
            $session            = Mage::getSingleton('core/session');
            $customerSession    = Mage::getSingleton('customer/session');
            $email              = (string) $this->getRequest()->getPost('email');

            try {
                if (!Zend_Validate::is($email, 'EmailAddress')) {
                    Mage::throwException($this->__('Please enter a valid email address.'));
                }

                if (Mage::getStoreConfig(Mage_Newsletter_Model_Subscriber::XML_PATH_ALLOW_GUEST_SUBSCRIBE_FLAG) != 1 && 
                    !$customerSession->isLoggedIn()) {
                    Mage::throwException($this->__('Sorry, but administrator denied subscription for guests. Please <a href="%s">register</a>.', Mage::helper('customer')->getRegisterUrl()));
                }

                $ownerId = Mage::getModel('customer/customer')
                        ->setWebsiteId(Mage::app()->getStore()->getWebsiteId())
                        ->loadByEmail($email)
                        ->getId();
                if ($ownerId !== null && $ownerId != $customerSession->getId()) {
                    Mage::throwException($this->__('This email address is already assigned to another user.'));
                }

                $status = Mage::getModel('newsletter/subscriber')->subscribe($email);
                if ($status == Mage_Newsletter_Model_Subscriber::STATUS_NOT_ACTIVE) {
                    $session->addSuccess($this->__('Confirmation request has been sent.'));
                }
                else {
                    $session->addSuccess($this->__('Thank you for your subscription.'));
                }
            }
            catch (Mage_Core_Exception $e) {
                $session->addException($e, $this->__('There was a problem with the subscription: %s', $e->getMessage()));
            }
            catch (Exception $e) {
                $session->addException($e, $this->__('There was a problem with the subscription.'));
            }
        }
        //Adding class 'subscription' for further messages filtration
        $session->getMessages()->getLastAddedMessage()->setIdentifier('subscription');

        $this->_redirectReferer();
    }
}

The only difference from an original newAction() method is adding a specific identifier property to the created message. If we look closely to Mage_Core_Model_Message_Abstract, we can see that it has some unused properties. Protected property ‘type’ even doesn’t have its own setter and getter methods. It is unused and can not be accessed. Of course, we can try to extend the model to get our own property. But this model is abstract, so we should extend four (Error, Notice, Success, Warning) models in that case. That is why just use the ‘identifier’ property from the existing model. We have to override much of the standard functionality anyway, so let’s leave this model as it is.

Next step will be the overriding messages block, where we can filter out our messages. Firstly, we will need to exclude our desired messages by identifier via extending Mage_Core_Block_Messages. Let’s update our extension config file with the core messages bock override:

<global>
	<blocks>
		<extension_tag>
			<class>Company_Extension_Block</class>
		</extension_tag>
		<core>
			<rewrite>
				<messages>Company_Extension_Block_Core_Messages</messages>
			</rewrite>
		</core>
	</blocks>
</global>

Now, let’s put our new block file to project/app/code/local/Company/Extension/Block/Messages.php:

class Company_Extension_Block_Core_Messages extends Mage_Core_Block_Messages
{
    public function _prepareLayout()
    {
        $this->addMessages(Mage::getSingleton('core/session')->getMessages(false));
        Mage_Core_Block_Template::_prepareLayout();
    }
    /**
     * Add messages to display
     *
     * @param Mage_Core_Model_Message_Collection $messages
     * @return Mage_Core_Block_Messages
     */
    public function addMessages(Mage_Core_Model_Message_Collection $messages)
    {
        foreach ($messages->getItems() as $message) {
            if($message->getIdentifier() != 'subscription')
            {
                $this->getMessageCollection()->add($message);
            }
        }
        return $this;
    }
}

So, we simply update addMessages() method not to add messages with a desired identifier to the current collection and updated _prepareLayout method changing getMessages() method call parameter to FALSE. This means that messages session will not be cleared after the messages extraction. Also, pay attention please, that we called original Mage_Core_Block_Template::_prepareLayout() instead of calling parent::_prepareLayout(), which could destroy our session. Now, let’s make another block for our newsletter messages:

class Company_Extension_Block_Subscription_Messages extends Mage_Core_Block_Messages
{
    public function _prepareLayout()
    {
        $this->addMessages(Mage::getSingleton('core/session')->getMessages(true));
        Mage_Core_Block_Template::_prepareLayout();
    }
    /**
     * Add subscription messages to display
     *
     * @param Mage_Core_Model_Message_Collection $messages
     * @return Mage_Core_Block_Messages
     */
    public function addMessages(Mage_Core_Model_Message_Collection $messages)
    {
        foreach ($messages->getItems() as $message) {
            if($message->getIdentifier() == 'subscription')
            {
                $this->getMessageCollection()->add($message);
            }
        }
        return $this;
    }
}

Everything is pretty same here, except of adding ONLY desired messages to the messages collection and unsetting messages session in _prepareLayout() method. If you want to use more then two places for messages output, simply put TRUE on getMessages only in the LAST block you call.

Our last step will be calling messages block for the newsletter (in our case) in the desired place. Update extension config.xml node with the layout declaration:

<layout>
	<updates>
		<extension_tag>
			<file>company_extension.xml</file>
		</extension_tag>
	</updates>
</layout>

Now, create that layout here app/design/frontend/package/theme/layout/company_extension.xml :

<?xml version="1.0"?>
<layout version="0.1.0">
	<default>
		<reference name="target.block.name">
			<block type="extension_tag/subscription_messages" name="subscription_messages" />
		</reference>
	</default>
</layout>

All what is left – to call our block in “target.block.name” template file:

<?php echo $this->getChildHtml('subscription_messages') ?>

That’s all! Thanks for reading us, we hope you will find this information useful.