1

How to restrict cart in magento to only one product per customer?

2
3

Although the solution provided by @Jigs-Parmar should also work, you can do this using plugin also.

Below are the major steps:

app/code/SR/Stackexchange/etc/frontend/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\Checkout\Model\Cart">
        <plugin name="SR_Stackexchange::add_to_cart" type="SR\Stackexchange\Plugin\Checkout\Model\Cart" sortOrder="1"/>
    </type>
</config>

app/code/SR/Stackexchange/Plugin/Checkout/Model/Cart.php


namespace SR\Stackexchange\Plugin\Checkout\Model;

use Magento\Catalog\Model\Product;

class Cart
{
    /**
     * @var \Magento\Checkout\Model\Session
     */
    protected $checkoutSession;

    /**
     * Cart constructor.
     *
     * @param \Magento\Checkout\Model\Session $checkoutSession
     */
    public function __construct(
        \Magento\Checkout\Model\Session $checkoutSession
    ) {
        $this->checkoutSession = $checkoutSession;
    }

    /**
     * @param \Magento\Checkout\Model\Cart $subject
     * @param \Closure $proceed
     * @param $productInfo
     * @param null $requestInfo
     * @return mixed
     */
    public function aroundAddProduct(
        \Magento\Checkout\Model\Cart $subject,
        \Closure $proceed,
        $productInfo,
        $requestInfo = null
    ) {

        if ($productInfo instanceof Product) {
            $productId = $productInfo->getId();
        } elseif (is_int($productInfo) || is_string($productInfo)) {
            $productId = $productInfo;
        } else {
            return $proceed($productInfo, $requestInfo);
        }

        $isSameProduct = 0;
        $quote = $this->checkoutSession->getQuote();
        $items = $quote->getAllItems();
        foreach ($items as $item) {
            if($productId == $item->getProductId()) {
                $isSameProduct = 1;
                break;
            }
        }

        if($isSameProduct) {
            return $proceed($productInfo, $requestInfo);
        }

        $quote->removeAllItems()->save();
        return $proceed($productInfo, $requestInfo);
    }
}

Now you need to modify when merging quote after login:

app/code/SR/Stackexchange/etc/frontend/events.xml


<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="sales_quote_merge_before">
        <observer name="SR_Stackexchange_sales_quote_merge_before" instance="SR\Stackexchange\Observer\SalesQuoteMergeBefore"/>
    </event>
</config>

app/code/SR/Stackexchange/Observer/SalesQuoteMergeBefore.php


namespace SR\Stackexchange\Observer;

use Magento\Framework\Event\Observer as EventObserver;
use Magento\Framework\Event\ObserverInterface;

class SalesQuoteMergeBefore implements ObserverInterface
{
    /**
     * Avoid shopping carts merging on customer login
     *
     * @param EventObserver $observer
     */
    public function execute(EventObserver $observer)
    {
        if ($observer->getSource()->hasItems()) {
            $currentQuote = $observer->getSource();
            $productId = 0;
            foreach ($currentQuote->getAllVisibleItems() as $item) {
                $productId = $item->getProductId();
            }

            if (is_object($observer->getQuote()) && $observer->getQuote()->getId()) {
                $oldQuote = $observer->getQuote();
                $hasSameProduct = 0;
                foreach ($oldQuote->getAllVisibleItems() as $item) {
                    if($productId == $item->getProductId()) {
                        $hasSameProduct = 1;
                    }
                }
                if($hasSameProduct == 0) {
                    $observer->getQuote()->removeAllItems();
                }
            }
        }
    }
}

Clear cache.

Note: You can change 'SR/Stackexchange' your own module name

Note: I assume that you want to remove last added item every time when new item is added to the cart. Please make necessary changes in plugin class if you want to keep the old item and only show the message to customer that new item can not be added.

Let me know if it doesn't work or if you have query.

1

Since no answer is accepted, Around plugins (used in Mohit's answer) are advised against by Magento and Jigs' answer seems excessive and might not work if other extensions are hooking into the same observer, I figured I'd share my own answer.

I'm assuming you know how to build a module's basics (registration.php, module.xml, etc.)

This module redirects the user to the cart and shows an error, prompting the user to first finish this order and place another.

Daan/LimitCartItems/etc/frontend/events.xml

<?xml version="1.0" encoding="UTF-8"?>
<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="daan_limit_items_in_cart_checkout_before" instance="Daan\LimitCartItems\Observer\Checkout\Cart\Add" />
    </event>
</config>

Daan/LimitCartItems/Observer/Checkout/Cart/Add.php

<?php

namespace Daan\LimitCartItems\Observer\Checkout\Cart;

use Magento\Checkout\Model\Session;
use Magento\Framework\App\ActionFlag;
use Magento\Framework\App\Response\RedirectInterface as ResponseRedirect;
use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\Message\ManagerInterface as MessageManager;

class Add implements ObserverInterface
{
    /** @var Session $session */
    private $session;

    /** @var ActionFlag $flag */
    private $flag;

    /** @var ResponseRedirect $redirect */
    private $redirect;

    /** @var MessageManager $messageManager */
    private $messageManager;

    /**
     * Add constructor.
     *
     * @param Session          $session
     * @param ActionFlag       $flag
     * @param ResponseRedirect $redirect
     * @param MessageManager   $messageManager
     */
    public function __construct(
        Session $session,
        ActionFlag $flag,
        ResponseRedirect $redirect,
        MessageManager $messageManager
    ) {
        $this->session         = $session;
        $this->flag            = $flag;
        $this->redirect        = $redirect;
        $this->messageManager  = $messageManager;
    }

    /**
     * @param Observer $observer
     *
     * @return $this|void
     * @throws \Magento\Framework\Exception\LocalizedException
     * @throws \Magento\Framework\Exception\NoSuchEntityException
     */
    public function execute(Observer $observer)
    {
        $quote      = $this->session->getQuote();
        $itemsCount = $quote->getItemsCount();

        if ($itemsCount >= 1) {
            $request = $observer->getRequest();
            $request->setParams([
                'product'    => false,
                'return_url' => $this->redirect->getRefererUrl(),
                'backUrl'    => $this->redirect->getRefererUrl()
            ]);

            $this->messageManager->addErrorMessage(
                __('Only one product per purchase allowed. Please complete this order first and place another order afterwards.')
            );

            $action = $observer->getEvent()->getControllerAction();
            $this->flag->set('', \Magento\Framework\App\ActionInterface::FLAG_NO_DISPATCH, true);
            $action->getResponse()->setRedirect('/checkout/cart');

            return $this;
        }
    }
}
1
  • 1
    This indeed should be the accepted answer Aug 18 '20 at 12:51
0

create event.xml file under etc folder

and write below code

<event name="controller_action_predispatch_checkout_cart_add">
      <observer name="restrict_sales_model_cart_add_before" instance="Vendor\Module\Observer\Cartadd" />
  </event>

now create Cartadd.php under Obsever folder

<?php
namespace Vendor\Module\Observer;

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

class Cartadd implements ObserverInterface {

    protected $_urlManager;
    protected $_checkoutSession;
    protected $_cart;
    protected $_messageManager;
    protected $_redirect;
    protected $_request;
    protected $_response;
    protected $_responseFactory;
    protected $_resultFactory;
    protected $_scopeConfig;
    protected $_product;

    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, \Magento\Framework\App\RequestInterface $request, \Magento\Framework\App\ResponseInterface $response, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Catalog\Model\Product $product, \Magento\Framework\App\ResponseFactory $responseFactory, \Magento\Framework\Controller\ResultFactory $resultFactory
    ) {
        $this->_urlManager = $urlManager;
        $this->_checkoutSession = $checkoutSession;
        $this->_redirect = $redirect;
        $this->_cart = $cart;
        $this->_messageManager = $messageManager;
        $this->_request = $request;
        $this->_response = $response;
        $this->_responseFactory = $responseFactory;
        $this->_resultFactory = $resultFactory;
        $this->_scopeConfig = $scopeConfig;
        $this->_product = $product;
    }

    public function execute(\Magento\Framework\Event\Observer $observer) {

        $controller = $observer->getControllerAction();
        $postValues = $this->_request->getPostValue();
        $cartQuote = $this->_cart->getQuote()->getData();
        $cartItemsCount = $this->_cart->getQuote()->getItemsCount();
        $cartItemsAll = $this->_cart->getQuote()->getAllItems();

        if($cartItemsCount > 0)
        {
            $observer->getRequest()->setParam('product', false);
            $observer->getRequest()->setParam('return_url', $this->_redirect->getRefererUrl());
            $observer->getRequest()->setParam('backUrl', $this->_redirect->getRefererUrl());
            $this->_messageManager->addError(__('Only 1 product Allowed to Purchase at a time.'));
        }
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.