1

Looking for ideas that implement best-practices and offer the best user-experience.

I'd like to check cart items for custom conditions when the checkout page (not the cart page) loads. I've looked at checking window.checkoutConfig data and using ajax, but I'd rather not go this route. I know the checkout is rendered entirely with UI components and javascript, but I'm looking for an event on which I can utilize an observer, or use a before/after/around plugin on something that happens when checkout page loads - something similar to Magento 2 - Validate Items Prior to Placing an Ordeer. Also, I don't want to wait until 'place order' to make this check. It would be nice to run through the cart items on page load of the checkout, check conditions, and maybe removing items from cart based on these conditions, or at least throwing an exception, and requesting the user remove items from cart.

3 Answers 3

0

There will be multiple solution for this, I am sharing what I am aware of plz check if that helps

Create a plugin for the method \Magento\Checkout\Model\Type\Onepage::initCheckout, This method will be called on the checkout controller load.

<type name="Magento\Checkout\Model\Type\Onepage">
<plugin name="validate_cart_items" type="Company\Module\Plugin\Magento\Checkout\Model\Type\OnepagePlugin"/></type>

In the plugin you can validate the cart items.

    public function afterInitCheckout(
        \Magento\Checkout\Model\Type\Onepage $subject
    ) {
        $quote = $subject->getQuote();
        // Validate the items here

    }
2
  • Thanks Moses! I'll check this out and get back to you.
    – user27786
    Nov 4, 2022 at 20:05
  • Update. As usual, there are multiple methods here. On first go I was able to accomplish what I needed with the after plugin, but my team preferred to go with an observable. I referenced magevision.com/blog/post/…, which is old, but still applicable to 2.4.5. I'll post my code as an answer to help others. Thanks again Moses!
    – user27786
    Nov 10, 2022 at 16:48
0

Here's what I ended up using, at least for now. Anyone have any feedback? Please let me know, thanks!

Ref - https://www.magevision.com/blog/post/validate-cart-before-checkout-magento-2

event.xml (in vendor/module/etc)

<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_index_index">
        <observer name="validate_cart_items_checkout" instance="Vendor\Module\Observer\ValidateOnCheckout " />
    </event>
</config>

observable (in vendor/module/Observer)

<?php

declare(strict_types=1);
namespace Vendor\Module\Observer;

use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Customer\Model\Session;
use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\Exception\CouldNotSaveException;
use Magento\Framework\Exception\InputException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Message\ManagerInterface;
use Magento\Framework\App\Response\RedirectInterface;
use Magento\Quote\Api\CartItemRepositoryInterface;
use Magento\Quote\Model\QuoteRepository;

class ValidateOnCheckout implements ObserverInterface
{
    public function __construct(
        private readonly Session $customerSession,
        private readonly ProductRepositoryInterface $productRepository,
        private readonly CustomerRepository $customerRepository,
        private readonly ManagerInterface $messageManager,
        private readonly RedirectInterface $redirect,
        private readonly CartItemRepositoryInterface $repository,
        private readonly QuoteRepository $cart
    ) {}

    /**
     * @throws NoSuchEntityException
     * @throws CouldNotSaveException
     */
    public function execute(Observer $observer)
    {
        $controller = $observer->getControllerAction();

        //if not logged in redirect to login. 
        if (!$this->customerSession->isLoggedIn()) {
            $this->redirect->redirect($controller->getResponse(), 'customer/account/login');
        }

        //get quote/cart from customer session
        $quote = $this->cart->getForCustomer($this->customerSession->getId());

        //get quote items. support simple and configurable
        $items = $this->repository->getList($quote->getId());
        $items_to_process = $this->getQuoteItems($items);

        //process items, remove from cart if needed. if removed from cart, redirect back to cart and show message(s)
        $redirect = false;
        foreach ($items_to_process as $item_to_process) {
            // validate item can do whatever you need
            if ($this->validateItem($item_to_process)) continue;
            $redirect = true;
            try {
                //delete quote item
                $this->repository->deleteById($quote->getId(), $item_to_process->getItemId());
            } catch (\Magento\Framework\Exception\LocalizedException $exception) {
                $this->messageManager->addErrorMessage($exception->getMessage());
            } catch (\Exception $exception) {
                $this->messageManager->addExceptionMessage($exception, __('We are unable update the shopping cart. Please remove %1 from your cart', $item_to_process->getName()));
            }
            $this->messageManager->addNoticeMessage(__('%1 has been removed from your cart because [your validation reason here]', $item_to_process->getName()));
        }
        
        //redirect back to cart
        if ($redirect) {
            $this->redirect->redirect($controller->getResponse(), 'checkout/cart');
        }
    }

    /**
     * Get quote items from simple and configurable
     * @param $items
     * @return array
     */
    private function getQuoteItems($items): array
    {
        $results = array();
        foreach ($items as $item) {
            if ($children = $item->getChildren()) {
                foreach ($children as $child) {
                    $results [] = $child;
                }
            } else {
                $results [] = $item;
            }
        }
        return $results;
    }

    private function validteItem($item): bool
    {
        $productId = $item->getProductId();
        try {
            $product = $this->productRepository->getById($productId);
        } catch(NoSuchEntityException $e) {
            throw new NoSuchEntityException( __('Product not found'));
        }
      
        //check stuff, return true or false for item
    }


  
}
0

I think the most effective way is to use the same pathway as the salable/stock validation because if you add errors there it will prevent proceeding to checkout from cart. Here’s an example how you can do that by creating an observer:

etc/events.xml

<event name="sales_quote_item_qty_set_after">
    <observer name="My_Module_ExampleValidation" instance="My\Module\Observer\ExampleValidation"/>
</event>

Observer/ExampleValidation.php

class ExampleValidation implements \Magento\Framework\Event\ObserverInterface
{
    /**
     * {@inheritDoc}
     */
    public function execute(\Magento\Framework\Event\Observer $observer)
    {
        /** @var \Magento\Quote\Api\Data\CartItemInterface & \Magento\Quote\Model\Quote\Item $item */
        $item = $observer->getItem();
        $quote = $item->getQuote();

        if (!$this->isValid($item)) {
            $quote->addErrorInfo('error', 'example', null, __('Quote has errors.'));
            $item->addErrorInfo('example', null, 'This item has an error.');
        }
    }

    private function isValid(\Magento\Quote\Model\Cart\Data\CartItem $item)
    {
        return true; // TODO implement
    }
}
1
  • Thanks for the feedback!
    – user27786
    Feb 9 at 16:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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