In this article, we’ll dive deep into the intricacies of messages in Magento 2. MessageManager (
Magento\Framework\Message\ManagerInterface) is used to show the website users messages like e.g. about a product added to a cart, wrong login/password, etc. How does Magento Message Manager work? Apart from walking through Message Manager, I’ll show you how to use HTML tags in messages e.g. to insert a link.
Magento Message Manager – A little bit of theory
Since the Message Manager is used mostly on the display layer, you have to remember to only use it where the layer is built, i.e. in the controller. Technically, it won’t affect the code if you add it directly to the model (and the message will display on the front end) but it will violate the MVC standards. If anyone ever has to work on your model and something goes wrong, it’s much better if they receive an exception than if they had to rely on a message to get information about the result of an action. It’s also important that you remember what exactly you want to display in the message.
Considering that the messages will be shown to customers, you wouldn’t want to show them a message like “ERROR 1045 (28000): Access denied for user ‘magento’@’localhost’ (using password: YES)”, but rather something like “Sorry! Something went wrong. Please try again later”.
So instead of catching different code exceptions, it’s better to add an error to logs and display a more user-friendly message.
Combining theory and practice
MessageManager uses session, collection, cookies, and customerData. The default MessageManager implementation,
Magento\Framework\Message\Manager, loads currently added messages (
Magento\Framework\Message\Manager::getMessages()) upon calling an add message method (like
addErrorMessage). If a given message group is saved in the session object, then it returns it. Otherwise, it creates a new collection and saves it in the session.
On the front end, the messages box is added in a default XML and it’s using Konckout.js and Cookies.
Magento\Theme\Controller\Result\MessagePlugin::afterRenderResult() plugin that intercepts
Magento\Framework\Controller\ResultInterface loads added messages and serializes them to the
mage-messages cookie. In turn,
Magento_Theme::messages.phtml template calls the
Magento_Theme/js/view/messages component. Which places messages in the block mentioned before. The reason for using cookies and Knockout.js is Full Page Cache – without it, the messages wouldn’t show up or every user would see the same message on a given page.
Note that if you use methods like
addSuccessMessage(), Magento won’t let you use HTML tags there. It is a solution to protect against XSS attacs. That’s why Magento introduced
Magento\Framework\View\Element\Message\InterpretationStrategyInterface. If you add a message with any HTML tag like you would normally do, the result may surprise you:
$this->messageManager->addSuccessMessage(__( 'You added %1 to your <a href="%2">shopping cart</a>', 'Product name', $this->_url->getUrl('checkout/cart') ));
Escaping happens on the PHP level so the widget receives a message without special HTML symbols (< and >).
To solve this problem, you can use so-called Complex Messages.
HTML in messages – Complex Messages
You will need to do three things:
- Call an appropriate method with the right parameters;
- Add your messages to
- Create a template responsible for message rendering.
Let’s say that you want to trigger the aforementioned message about adding a product to a cart and put a link to the cart inside it.
Calling a method (in the controller, in this case) looks like this:
$this->messageManager->addComplexSuccessMessage( 'ourAddToCartMessage', [ 'product_name' => $product->getName(), 'cart_url' => $this->_url->getUrl('checkout/cart'), ] );
The first parameter is an identifier (we’ll use it in di.xml) and the second one is an array with data that will be passed on to the block.
Next, add your message to MessageConfigurationsPool :
// file etc/frontned/di.xml <?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <type name="Magento\Framework\View\Element\Message\MessageConfigurationsPool"> <arguments> <argument name="configurationsMap" xsi:type="array"> <item name="ourAddToCartMessage" xsi:type="array"> <item name="renderer" xsi:type="const">Magento\Framework\View\Element\Message\Renderer\BlockRenderer::CODE</item> <item name="data" xsi:type="array"> <item name="template" xsi:type="string">Magently_Messages::messages/ourAddToCartMessage.phtml</item> </item> </item> </argument> </arguments> </type> </config>
The last thing you have to do is create a template (the path was specified earlier in the XML):
<?php /** @var \Magento\Framework\View\Element\Template $block */ ?> <?= $block->escapeHtml( __( 'You added %1 to your <a href="%2">shopping cart</a>', $block->getData('product_name'), $block->getData('cart_url') ), ['a'] ); ?>
As you can see, we used the
escapeHtml method but we pass a second parameter to it to tell it what attributes it should skip while escaping.
The result is just as expected:
// file: vendor/magento/module-theme/view/frontend/web/js/view/messages.js ... allowedTags: ['div', 'span', 'b', 'strong', 'i', 'em', 'u', 'a']
That’s it! Leave your thoughts and questions in the comments below.