In this article, we will show you how to add products to the cart programmatically in Magento 2. This is most useful when you want to add additional data to items in the cart, add products that require custom logic or simply add a gift for the customers purchasing your products.

This article assumes that:

  • We’re adding codes into a custom module named Magenest\Example with route ‘example’
  • Custom add-to-cart function is executed in a controller at Magenest\Example\Controller\Product

How to add products to cart using Quote Model

The controller AddToCart handles the GET request sent to path “example/product/addToCart” and adds a product to the cart using information stored in request parameters.

<?php
namespace Magenest\Example\Controller\Product;

use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
use Magento\Framework\App\Action\HttpGetActionInterface;
use Magento\Framework\Controller\ResultInterface;
use Magento\Framework\Exception\LocalizedException;

/**
* Class AddToCart
* @package Magenest\BookingReservation\Controller\Product
*/
class AddToCart extends Action implements HttpGetActionInterface
{
   /** @var \Magento\Checkout\Model\SessionFactory */
   private $checkoutSession;

   /** @var \Magento\Quote\Api\CartRepositoryInterface */
   private $cartRepository;

   /** @var \Magento\Catalog\Api\ProductRepositoryInterface */
   private $productRepository;

   /** @var \Magento\Framework\Serialize\Serializer\Json */
   private $json;

   /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable */
   private $configurableType;

   /**
     * AddToCart constructor.
     * @param Context $context
     * @param \Magento\Framework\Serialize\Serializer\Json $json
     * @param \Magento\Checkout\Model\SessionFactory $checkoutSession
     * @param \Magento\Quote\Api\CartRepositoryInterface $cartRepository
     * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
     * @param \Magento\ConfigurableProduct\Model\Product\Type\Configurable $configurableType
     */
   public function __construct(
       Context $context,
       \Magento\Framework\Serialize\Serializer\Json $json,
       \Magento\Checkout\Model\SessionFactory $checkoutSession,
       \Magento\Quote\Api\CartRepositoryInterface $cartRepository,
       \Magento\Catalog\Api\ProductRepositoryInterface $productRepository,
       \Magento\ConfigurableProduct\Model\Product\Type\Configurable $configurableType
   ) {
       $this->checkoutSession = $checkoutSession;
       $this->cartRepository = $cartRepository;
       $this->productRepository = $productRepository;
       $this->json = $json;
       $this->configurableType = $configurableType;
       parent::__construct($context);
   }

   /**
    * @return ResultInterface
    * @throws LocalizedException
    */
   public function execute()
   {
       $product = $this->productRepository->getById($this->getRequest()->getParam('id'));
       $qty = $this->getRequest()->getParam('qty');

       $session = $this->checkoutSession->create();
       $quote = $session->getQuote();
       $quote->addProduct($product, $qty);

       $this->cartRepository->save($quote);
       $session->replaceQuote($quote)->unsLastRealOrderId();
   }
}

You can add products to the cart using only their ID. The quantity parameter is optional: Quote Model will assume quantity is 1 if the parameter is null.

How to add product to cart with additional options

The addProduct function can adapt to both simple and advanced parameters format. This allows you to add additional options and data to cart items.

/**
* @throws LocalizedException
*/
public function addProductWithOptions()
{
   $productSku = $this->getRequest()->getParam('sku');
   $qty       = $this->getRequest()->getParam('qty');

   /**
    * You can specify custom options for a product before adding it to cart
    */
   $product = $this->productRepository->get($productSku);
   $product->addCustomOption('additional_options', $this->json->serialize([
       'custom_option_1' => [
           'label' => __("Custom Option 1"),
           'value' => 10
       ],
       'custom_option_2' => [
           'label' => __("Custom Option 2"),
           'value' => 20
       ]
   ]));

   /**
    * The second parameter of addProduct function can either be an integer, or a Magento DataObject
    * An integer will be interpreted as quantity added
    * Using a DataObject will allow adding custom data to cart items; these data will be saved and can be retrieved as info_buyRequest of quote/order items
    */
   $buyRequest = new \Magento\Framework\DataObject([
       'qty' => $qty,
       'related_product' => '',
       'custom_option' => '?'
   ]);

   $session = $this->checkoutSession->create();
   $quote = $session->getQuote();
   $quote->addProduct($product, $buyRequest);

   $this->cartRepository->save($quote);
   $session->replaceQuote($quote)->unsLastRealOrderId();
}

The ‘additional_options’ of Product will be visible on the Cart page:

Additional options of product on Cart page
Additional options of product on the Cart page

How to add product with special types to cart

Adding some products with special types to the cart may require providing additional options. Assuming you know the specific parameter values required, you can also add these products to the cart. Here’s the general format for adding a Bundle Product to the cart:

/**
* @throws LocalizedException
*/
public function addSpecialProduct()
{
   $productId = $this->getRequest()->getParam('id');
   $product = $this->productRepository->getById($productId);

   $buyRequest = new \Magento\Framework\DataObject([
       // super_group is used to set quantity for sub-items in grouped product
       'super_group' => [
           10 => "1", // value is pass as $sub-productId => $qty
           20 => "2"
       ],

       // adding bundle product require two options: 'bundle_option' and 'bundle_option_qty'
       // both options are passed as indexed array, and items in two array will be matched using their index
       'bundle_option'     => ["2", "4", "5"], // items are added using bundle product 'selection_id', not actual product id
       'bundle_option_qty' => ["1", "2", "3"],

       // configurable product options are passed using 'super_attribute' option
       'super_attribute' => [
           143 => "168", // the format are $optionId => $optionValue
           93 => "58"
       ]
   ]);

   $session = $this->checkoutSession->create();
   $quote = $session->getQuote();
   $quote->addProduct($product, $buyRequest);

   $this->cartRepository->save($quote);
   $session->replaceQuote($quote)->unsLastRealOrderId();
}

Each special product type has its own functions to get special attributes. Refer to the Type Model for those products to learn how to add them manually.

For example, configurable products attributes can be pulled using function getConfigurableAttributes() or getConfigurableAttributesAsArray(). This allows us to add a configurable product to the cart, using their simple SKU:

/**
* @throws LocalizedException
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
private function addConfigurableProduct()
{
   $childId = $this->getRequest()->getParam('id');
   $childProduct = $this->productRepository->getById($childId);

   /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable configurableType */
   $parentId = $this->configurableType->getParentIdsByChild($childId);
   if (($parentId = reset($parentId)) !== false) {
       $parentProduct = $this->productRepository->getById($parentId);
       $productAttributeOptions = $this->configurableType->getConfigurableAttributesAsArray($parentProduct);

       $options = [];
       foreach ($productAttributeOptions as $option) {
           $options[$option['attribute_id']] =  $childProduct->getData($option['attribute_code']);
       }
       $buyRequest = new \Magento\Framework\DataObject(['super_attribute' => $options]);

       $session = $this->checkoutSession->create();
       $quote = $session->getQuote();
       $quote->addProduct($parentProduct, $buyRequest);

       $this->cartRepository->save($quote);
       $session->replaceQuote($quote)->unsLastRealOrderId();
   }
}

Configurable products should always be added as Configurable SKU with custom options, instead of their simple SKU:

add products to cart programmatically
Configurable SKU for configurable products

Trigger mini cart update when adding to cart programmatically

Unlike the Cart page which requests the latest data every time it’s accessed, the mini cart content is cached on the client-side using Magento’s customer-data.js library.

This cache can only be cleared by executing Javascript on the client machine; since the server normally can’t request clearing data saved on the client. Therefore cache invalidating and reloading for customer-data.js generally happen when an event is triggered on the client-side, like a successful AJAX request.

The mini cart can be configured to update whenever an AJAX request to addToCart endpoint is complete:

require([
   'jquery',
   'mage/url',
   'Magento_Customer/js/customer-data'
], function ($, urlBuilder, customerData) {
   $.ajax({
       type: 'POST',
       data: {
           product_id: ...,
           qty: ...
       },
       url: urlBuilder.build('example/product/addToCart'),
       complete: function () {
           // you might not need this section if you're using sections.xml instead
           customerData.invalidate(['cart']);
           customerData.reload(['cart'], true);
       },
       success: ...,
       error: ...
   })
});

However, calling these functions directly every time is not very efficient. Magento allows configuring these endpoint server-side using sections.xml configuration file instead:

Magenest/Example/etc/frontend/sections.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Customer:etc/sections.xsd">
   <action name="example/product/addToCart">
       <section name="cart"/>
   </action>
</config>

Now all AJAX requests sent to ‘example/product/addToCart’ using Magento 2 AJAX library will trigger reloading ‘cart’ data, without manually adding callbacks to AJAX requests.

Conclusion

Above is our complete guide on how to add products to the cart programmatically. We hope that our guide has covered the answers to your problems with this process. However, if you still have any questions, please drop them down in the comment section or check out our tech guides.