Magento – Magento 2: Observer Message with Yes/No Button & Functionality

cartcustomevent-observermagento2

I'm using Magento 2 CE Version 2.1.0.

I have a custom attribute called Type with value A or B. User can only add 1 Product of Type A in the Cart.

If a user is going to Add Second time Same or Other product of Type A. Then Observer will give a message "This type of product is already exists. Would you like to overwrite?".

Able to display a message. Now I have to give 2 Buttons with a message "Yes" / "No". If clicked on "Yes" it will replace product in cart with current one & on "No" will do nothing.

app\code\Custom\Module\etc\frontend\events.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="controller_action_predispatch_checkout_cart_add">
        <observer name="check_shopping_cart_page" instance="Custom\Module\Observer\CheckCartObserver" />
    </event>
</config>

app\code\Custom\Module\Observer\CheckCartObserver.php

namespace Custom\Module\Observer;

use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\App\Request\DataPersistorInterface;
use Magento\Framework\App\ObjectManager;

class CheckCartObserver implements ObserverInterface {

    protected $_urlManager;
    protected $_checkoutSession;
    protected $_cart;
    protected $messageManager;

    /**
     * @var \Magento\Framework\App\Response\RedirectInterface
     */
    protected $redirect;

    public function __construct(
    \Magento\Framework\UrlInterface $urlManager, \Magento\Checkout\Model\Session $checkoutSession, \Magento\Framework\App\Response\RedirectInterface $redirect, \Magento\Checkout\Model\Cart $cart, \Magento\Framework\Message\ManagerInterface $messageManager
    ) {
        $this->_urlManager = $urlManager;
        $this->_checkoutSession = $checkoutSession;
        $this->redirect = $redirect;
        $this->_cart = $cart;
        $this->messageManager = $messageManager;
    }

    public function execute(\Magento\Framework\Event\Observer $observer) {
        $actionName = $observer->getEvent()->getRequest()->getFullActionName();
        $controller = $observer->getControllerAction();
        $postValues = $controller->getRequest()->getPostValue();
        $cartQuote = $this->_cart->getQuote()->getData();

        // START IF YES CLICKED THEN REPLACE PRODUCT
        if (isset($postValues['replaceYes'])) {
            $this->messageManager->addError(__('Replace Yes Clicked'));
            $this->redirect->redirect($controller->getResponse(), $this->redirect->getRefererUrl());
        }
        // STOP IF YES CLICKED THEN REPLACE PRODUCT
        // START IF NO CLICKED THEN REPLACE PRODUCT
        if (isset($postValues['replaceNo'])) {
            $this->messageManager->addError(__('Replace No Clicked'));
            $this->redirect->redirect($controller->getResponse(), $this->redirect->getRefererUrl());
        }
        // STOP IF NO CLICKED THEN REPLACE PRODUCT

        // START TO CHECK IF TYPE A & ONLY 1 PRODUCT EXIST
        $cartItemsCount = $this->_cart->getQuote()->getItemsCount();
        $cartItemsAll = $this->_cart->getQuote()->getAllItems();


        foreach ($cartItemsAll as $item) {

            $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
            $product = $objectManager->create('Magento\Catalog\Model\Product')->load($item->getProduct()->getId());
            $type = $product->getAttributeText('type');

            if ($type == 'A' && $cartItemsCount == 1) {
                $this->messageManager->addError(__('A Type of product is already exist. Would you like to replace it? <form method="post"><input type="submit" name="replaceYes" value="Yes" /> <input type="submit" name="replaceNo" value="No" /></form>'));
                $observer->getRequest()->setParam('product', false); // Will not add product to Cart
                $this->redirect->redirect($controller->getResponse(), $this->redirect->getRefererUrl());
            }
        }
        // END TO CHECK IF TYPE A & ONLY 1 PRODUCT EXIST
    }
}

If this is not feasible, then what will be word around solution?

Best Answer

I do not recommend to attach an Observer to a controller_action_predispatch_checkout_cart_add event. Your requirement is more business-related than system/platform e.g. Session check or ACL/Permissions check so I wouldn't use controller observer.

Also, observer seems like responsible for lot's of things here: validation, request/response manipulations and html rendering.

My approach would be the following:

  1. Create an after plugin for Magento\Checkout\CustomerData\ItemPool::getItemData() method
  2. Inside the returning array of product data I would add additional "type" attribute per product with value "A" or "B"
  3. Load Type attribute value as hidden form value on product page.
  4. Create custom JavaScript component which will check "Add to Cart" submit event and verify if hidden Type attribute value appears to be in the list of existing products added to a cart:

    var cartData = customerData.get('cart');
    var items = cartData()['items'];
    var result = false;
    $.each(items, function(index, productItem) {
        if (productItem.type === productHiddenType) {
            result = true;
        }
    });
    if (result) {
        modalPopup.show(); //show form with Yes and No
    }
    
  5. Show popup with additional form

  6. Submit "Add to Cart" form in case customer clicks "Yes"
  7. Validation of Quote/Cart should happen on Quote::beforeSave plugin
Related Topic