cancel
Showing results for 
Search instead for 
Did you mean: 

Refactor Cart Add and Index classes to allow customization of message texts

Refactor Cart Add and Index classes to allow customization of message texts

Feature request from leoquijano, posted on GitHub May 18, 2016

While implementing a custom theme using Magento 2, I found out that there's currently no support for easy customization of Add to Cart message texts.

In the Magento\Checkout\Controller\Cart\Add class, the following method is called to add a product to the cart:

/**
 * Add product to shopping cart action
 *
 * @return \Magento\Framework\Controller\Result\Redirect
 * @SuppressWarnings(PHPMD.CyclomaticComplexity)
 */
public function execute()
{
    if (!$this->_formKeyValidator->validate($this->getRequest())) {
        return $this->resultRedirectFactory->create()->setPath('*/*/');
    }

    $params = $this->getRequest()->getParams();
    try {
        if (isset($params['qty'])) {
            $filter = new \Zend_Filter_LocalizedToNormalized(
                ['locale' => $this->_objectManager->get('Magento\Framework\Locale\ResolverInterface')->getLocale()]
            );
            $params['qty'] = $filter->filter($params['qty']);
        }

        $product = $this->_initProduct();
        $related = $this->getRequest()->getParam('related_product');

        /**
         * Check product availability
         */
        if (!$product) {
            return $this->goBack();
        }

        $this->cart->addProduct($product, $params);
        if (!empty($related)) {
            $this->cart->addProductsByIds(explode(',', $related));
        }

        $this->cart->save();

        /**
         * @todo remove wishlist observer \Magento\Wishlist\Observer\AddToCart
         */
        $this->_eventManager->dispatch(
            'checkout_cart_add_product_complete',
            ['product' => $product, 'request' => $this->getRequest(), 'response' => $this->getResponse()]
        );

        if (!$this->_checkoutSession->getNoCartRedirect(true)) {
            if (!$this->cart->getQuote()->getHasError()) {
                $message = __(
                    'You added %1 to your shopping cart.',
                    $product->getName()
                );
                $this->messageManager->addSuccessMessage($message);
            }
            return $this->goBack(null, $product);
        }
    } catch (\Magento\Framework\Exception\LocalizedException $e) {
        if ($this->_checkoutSession->getUseNotice(true)) {
            $this->messageManager->addNotice(
                $this->_objectManager->get('Magento\Framework\Escaper')->escapeHtml($e->getMessage())
            );
        } else {
            $messages = array_unique(explode("\n", $e->getMessage()));
            foreach ($messages as $message) {
                $this->messageManager->addError(
                    $this->_objectManager->get('Magento\Framework\Escaper')->escapeHtml($message)
                );
            }
        }

        $url = $this->_checkoutSession->getRedirectUrl(true);

        if (!$url) {
            $cartUrl = $this->_objectManager->get('Magento\Checkout\Helper\Cart')->getCartUrl();
            $url = $this->_redirect->getRedirectUrl($cartUrl);
        }

        return $this->goBack($url);

    } catch (\Exception $e) {
        $this->messageManager->addException($e, __('We can\'t add this item to your shopping cart right now.'));
        $this->_objectManager->get('Psr\Log\LoggerInterface')->critical($e);
        return $this->goBack();
    }
}

It can be seen here two particular places where messages are hardwired. The first one is the success message:

$message = __(
    'You added %1 to your shopping cart.',
    $product->getName()
);
$this->messageManager->addSuccessMessage($message);

The second one is the error message:

} catch (\Exception $e) {
    $this->messageManager->addException($e, __('We can\'t add this item to your shopping cart right now.'));
    $this->_objectManager->get('Psr\Log\LoggerInterface')->critical($e);
    return $this->goBack();
}

Some websites require different copy for the Add to Cart behavior (both for success and error messages), so ideally theme developers should be able to customize that behavior. While the optimal approach would be to configure this in layout or template files, I think that a relatively simple solution is refactoring the Controller code, like this:

if (!$this->cart->getQuote()->getHasError()) {
  $message = $this->getSuccessMessage($product);
  $this->messageManager->addSuccessMessage($message);
}
} catch (\Exception $e) {
  $this->messageManager->addException($e, $this->getErrorMessage());
  $this->_objectManager->get('Psr\Log\LoggerInterface')->critical($e);
  return $this->goBack();
}
/**
 * Gets the message used when a product is successfully added to the cart.
 * @param $product
 * @return \Magento\Framework\Phrase
 */
protected function getSuccessMessage($product)
{
    return __(
      'You added %1 to your shopping cart.',
      $product->getName()
    );
}

/**
 * Gets the message used when there's a problem adding an item to the cart.
 * @return \Magento\Framework\Phrase
 */
protected function getErrorMessage()
{
    return __('We can\'t add this item to your shopping cart right now.');
}

This would allow theme developers to override this controller without overriding the main execute method, which can be risky and has a higher maintenance cost.

3 Comments
New Member

Comment from leoquijano, posted on GitHub May 18, 2016

For example, this is my version of these two methods, which replaces the word 'Cart' for 'Bag'. Other websites might use 'Basket', or some other copy:

/**
 * Gets the message used when a product is successfully added to the cart.
 * @param $product
 * @return \Magento\Framework\Phrase
 */
protected function getSuccessMessage($product)
{
    return __(
      'You added %1 to your bag.',
      $product->getName()
    );
}

/**
 * Gets the message used when there's a problem adding an item to the cart.
 * @return \Magento\Framework\Phrase
 */
protected function getErrorMessage()
{
    return __('We can\'t add this item to your bag right now.');
}
New Member

Comment from leoquijano, posted on GitHub May 19, 2016

An additional place where this can be improved is the Magento\Checkout\Controller\Cart\Index class:

/**
 * Shopping cart display action
 *
 * @return \Magento\Framework\View\Result\Page
 */
public function execute()
{
    $resultPage = $this->resultPageFactory->create();
    $resultPage->getConfig()->getTitle()->set(__('Shopping Cart'));
    return $resultPage;
}

It can be refactored like this:

/**
 * Shopping cart display action
 *
 * @return \Magento\Framework\View\Result\Page
 */
public function execute()
{
    $resultPage = $this->resultPageFactory->create();
    $resultPage->getConfig()->getTitle()->set($this->getTitle());
    return $resultPage;
}

/**
 * Returns the title for the shopping cart page.
 * @return \Magento\Framework\Phrase
 */
public function getTitle()
{
    return __('Shopping Cart');
}
New Member

Comment from leoquijano, posted on GitHub May 19, 2016

And then theme developers can override the getTitle method:

/**
 * Returns the title for the shopping cart page.
 * @return \Magento\Framework\Phrase
 */
public function getTitle()
{
    return __('Your Bag');
}