In this article we will address the issue that is widely discussed at the Magento 2 thematic forums — when you add a product to the user’s cart programmatically, the cost of the product becomes zero. To solve this problem, we have created a module that provides a convenient interface for adding items to the selected basket.
Install the test instance Magento 2.3.0 with sample data.
1 2 3 4 5 6 |
composer create-project --repository=https://repo.magento.com/ "magento/project-community-edition":"2.3.0" /var/www/magento-2.3.0.test php bin/magento setup:install --base-url="http://magento-2.3.0.test/" --db-host="mysql" --db-name="magento_2_3_0" --db-user="root" --db-password="root" --admin-firstname="admin" --admin-lastname="admin" --admin-email="[email protected]" --admin-user="admin" --admin-password="Admin123" --language="en_US" --currency="USD" --use-rewrites="1" --backend-frontname="backadmin" --timezone="UTC" php bin/magento sampledata:deploy php bin/magento setup:upgrade php bin/magento indexer:reindex |
At the end of the article you will find the code of the developed module. The module creates a page in the Magento administration panel with an interface for adding items to the cart with an indication of the SKU (if necessary) and quantity. The action to add goods to the basket is performed in the action Index. Bear in mind there still can be difficulties with the use of different types of goods (Simple, Virtual, Configurable, Downloadable, Bundle, Grouped), because for certain the parameter of the addProduct function of the Quote object should be formed in a special way.
Magento Webdesign
Take your online store to the next level with BelVG Magento Webdesign
Visit the pageHaving tested all variations of types of goods, we found no problems with the price. Therefore, we conclude that this problem is not connected with the standard delivery of Magento 2.3.0, but rather lies in mistakes in the code or a conflict with a third-party module. Ideally, each case when a problem with the price during the programmatic addition of goods to the basket is individual and must be considered separately to determine and eliminate an underlying cause.
Module code is available on GitHub: https://github.com/belvg-public/AddToCart
Partner With Us
Let's discuss how to grow your business. Get a Free Quote.app/code/BelVG/AddToCart/registration.php
1 2 3 4 5 6 7 8 9 |
<?php use \Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register( ComponentRegistrar::MODULE, 'BelVG_AddToCart', __DIR__ ); |
app/code/BelVG/AddToCart/etc/module.xml
1 2 3 4 5 6 |
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="BelVG_AddToCart" setup_version="1.0.0"> </module> </config> |
app/code/BelVG/AddToCart/etc/acl.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd"> <acl> <resources> <resource id="Magento_Backend::admin"> <resource id="Magento_Customer::customer"> <resource id="BelVG_AddToCart::add" title="Add to cart" translate="title" sortOrder="100" /> </resource> </resource> </resources> </acl> </config> |
app/code/BelVG/AddToCart/etc/adminhtml/routes.xml
1 2 3 4 5 6 7 8 9 |
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd"> <router id="admin"> <route id="belvg_addtocart" frontName="belvg_addtocart"> <module name="BelVG_AddToCart" /> </route> </router> </config> |
app/code/BelVG/AddToCart/etc/adminhtml/menu.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Backend:etc/menu.xsd"> <menu> <add id="BelVG_AddToCart::add" resource="BelVG_AddToCart::add" title="Add to cart" action="belvg_addtocart/index/index" module="BelVG_AddToCart" sortOrder="100" parent="Magento_Customer::customer"/> </menu> </config> |
app/code/BelVG/AddToCart/Block/AddToCart.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
<?php namespace BelVG\AddToCart\Block; use Magento\ConfigurableProduct\Model\Quote\Item\QuantityValidator\Initializer\Option\Plugin\ConfigurableProduct; use Magento\Framework\Data\Collection\AbstractDb; class AddToCart extends \Magento\Backend\Block\Template { private $quoteCollection; private $productCollection; public function __construct( \Magento\Catalog\Model\ResourceModel\Product\Collection $productCollection, \Magento\Quote\Model\ResourceModel\Quote\Collection $quoteCollection, \Magento\Backend\Block\Template\Context $context, array $data = [] ) { $this->productCollection = $productCollection; $this->quoteCollection = $quoteCollection; parent::__construct($context, $data); } public function getCartsList() { $this->quoteCollection->addOrder('entity_id', AbstractDb::SORT_ORDER_ASC); $list = []; foreach ($this->quoteCollection as $quote) { /** @var \Magento\Quote\Model\Quote $quote */ $list[$quote->getEntityId()] = $quote->getCustomerEmail() . ' [ID:' . $quote->getEntityId() . ']'; } return $list; } public function getProductsList() { $this->productCollection->addAttributeToSelect('name'); $this->productCollection->addOrder('entity_id', AbstractDb::SORT_ORDER_ASC); $list = []; foreach ($this->productCollection as $product) { /** @var \Magento\Catalog\Model\Product $product */ $haveOptions = in_array($product->getTypeId(), [ \Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE, ]); $list[$product->getEntityId()] = [ 'label' => $product->getName() . ' [ID: ' . $product->getEntityId() . ']', 'have_options' => $haveOptions ]; } return $list; } public function getOptionsUrl() { return $this->getUrl('belvg_addtocart/index/options'); } } |
app/code/BelVG/AddToCart/Controller/Adminhtml/Index/Index.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
<?php namespace BelVG\AddToCart\Controller\Adminhtml\Index; use Magento\Backend\App\Action; use Magento\Framework\Controller\ResultFactory; class Index extends Action { const ACL_RESOURCE = 'BelVG_AddToCart::add'; /** @var \Magento\Quote\Model\QuoteRepository $quoteRepository */ private $quoteRepository; /** @var \Magento\Catalog\Model\ProductRepository $productRepository */ private $productRepository; /** * Index constructor. * @param Action\Context $context */ public function __construct( \Magento\Quote\Model\QuoteRepository $quoteRepository, \Magento\Catalog\Model\ProductRepository $productRepository, \Magento\Catalog\Model\Product\Attribute\Repository $attributeRepository, \Magento\Backend\App\Action\Context $context ) { $this->quoteRepository = $quoteRepository; $this->productRepository = $productRepository; $this->attributeRepository = $attributeRepository; parent::__construct($context); } protected function _isAllowed() { $result = parent::_isAllowed(); $result = $result && $this->_authorization->isAllowed(self::ACL_RESOURCE); return $result; } public function execute() { $request = $this->getRequest(); if ($request->getParam('submit') == 'add') { $cartId = $request->getParam('cart_id'); $productId = $request->getParam('product_id'); $sku = $request->getParam('sku'); $qty = $request->getParam('qty'); $quote = $this->quoteRepository->get($cartId); $product = $this->productRepository->getById($productId); if ($quote && $product) { if ($product->getTypeId() == \Magento\GroupedProduct\Model\Product\Type\Grouped::TYPE_CODE) { /** @var \Magento\GroupedProduct\Model\Product\Type\Grouped $typedProduct */ $typedProduct = $product->getTypeInstance(); foreach ($typedProduct->getChildrenIds($productId, false) as $children) { foreach ($children as $id) { $_product = $this->productRepository->getById($id); $quote->addProduct($_product, $this->makeAddRequest($_product, $sku, $qty)); } } } else { $quote->addProduct($product, $this->makeAddRequest($product, $sku, $qty)); } $this->quoteRepository->save($quote); $this->messageManager->addErrorMessage(__('Add to cart successful.')); } else { $this->messageManager->addErrorMessage(__('Add to cart fail.')); } $this->_redirect('belvg_addtocart/index/index'); } /** @var \Magento\Backend\Model\View\Result\Page $resultPage */ $resultPage = $this->resultFactory->create(ResultFactory::TYPE_PAGE); $resultPage->setActiveMenu('Magento_Customer::customer'); $resultPage->getConfig()->getTitle()->prepend(__('Add to cart')); return $resultPage; } private function makeAddRequest(\Magento\Catalog\Model\Product $product, $sku = null, $qty = 1) { $data = [ 'product' => $product->getEntityId(), 'qty' => $qty ]; switch ($product->getTypeId()) { case \Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE: $data = $this->setConfigurableRequestOptions($product, $sku, $data); break; case \Magento\Bundle\Model\Product\Type::TYPE_CODE: $data = $this->setBundleRequestOptions($product, $data); break; } $request = new \Magento\Framework\DataObject(); $request->setData($data); return $request; } private function setConfigurableRequestOptions(\Magento\Catalog\Model\Product $product, $sku, array $data) { /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable $typedProduct */ $typedProduct = $product->getTypeInstance(); $childProduct = $this->productRepository->get($sku); $productAttributeOptions = $typedProduct->getConfigurableAttributesAsArray($product); $superAttributes = []; foreach ($productAttributeOptions as $option) { $superAttributes[$option['attribute_id']] = $childProduct->getData($option['attribute_code']); } $data['super_attribute'] = $superAttributes; return $data; } private function setBundleRequestOptions(\Magento\Catalog\Model\Product $product, array $data) { /** @var \Magento\Bundle\Model\Product\Type $typedProduct */ $typedProduct = $product->getTypeInstance(); $selectionCollection = $typedProduct->getSelectionsCollection($typedProduct->getOptionsIds($product), $product); $options = []; foreach ($selectionCollection as $proselection) { $options[$proselection->getOptionId()] = $proselection->getSelectionId(); } $data['bundle_option'] = $options; return $data; } } |
app/code/BelVG/AddToCart/Controller/Adminhtml/Index/Options.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
<?php namespace BelVG\AddToCart\Controller\Adminhtml\Index; use Magento\Backend\App\Action; class Options extends Action { const ACL_RESOURCE = 'BelVG_AddToCart::add'; /** @var \Magento\Catalog\Model\ProductRepository $productRepository */ private $productRepository; /** @var \Magento\Framework\Controller\Result\JsonFactory $jsonFactory */ private $jsonFactory; public function __construct( \Magento\Catalog\Model\ProductRepository $productRepository, \Magento\Framework\Serialize\Serializer\Json $jsonSerializer, \Magento\Framework\Controller\Result\JsonFactory $jsonFactory, \Magento\Backend\App\Action\Context $context ) { $this->productRepository = $productRepository; $this->jsonFactory = $jsonFactory; parent::__construct($context); } protected function _isAllowed() { $result = parent::_isAllowed(); $result = $result && $this->_authorization->isAllowed(self::ACL_RESOURCE); return $result; } public function execute() { $request = $this->getRequest(); if ($productId = $request->getParam('product_id')) { if ($product = $this->productRepository->getById($productId)) { /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable $typedProduct */ $typedProduct = $product->getTypeInstance(); $sku = []; foreach ($typedProduct->getChildrenIds($productId, false) as $group => $childrens) { foreach ($childrens as $childrenId) { $childProduct = $this->productRepository->getById($childrenId); $sku[] = $childProduct->getSku(); } } $sku = array_unique($sku); return $this->jsonFactory->create()->setData([ 'success' => true, 'data' => [ 'type' => $product->getTypeId(), 'product_id' => $productId, 'sku' => $sku, ] ]); } } return $this->jsonFactory->create()->setData([ 'success' => false ]); } } |
app/code/BelVG/AddToCart/view/adminhtml/layout/belvg_addtocart_index_index.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
<?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceBlock name="content"> <block class="BelVG\AddToCart\Block\AddToCart" template="BelVG_AddToCart::form.phtml"/> </referenceBlock> </body> </page> app/code/BelVG/AddToCart/view/adminhtml/templates/form.phtml <?php /** @var \BelVG\AddToCart\Block\AddToCart $block */ ?> <form method="post" enctype="multipart/form-data" action="<?= $this->getUrl('belvg_addtocart/index/index') ?>"> <fieldset class="fieldset admin__fieldset"> <div class="admin__field field admin__field-small required _required"> <label class="label admin__field-label" for="file"><span><?= __('Choose cart') ?></span></label> <div class="admin__field-control control" style="min-width: 300px;"> <select name="cart_id" title="<?= __('Choose cart') ?>" class="admin__control-text required-entry _required admin__control-text"> <?php foreach ($block->getCartsList() as $quoteId => $name): ?> <option value="<?= (int)$quoteId ?>"><?= $block->escapeHtml($name) ?></option> <?php endforeach ?> </select> </div> </div> <div class="admin__field field admin__field-small required _required"> <label class="label admin__field-label" for="product-selector"><span><?= __('Choose product') ?></span></label> <div class="admin__field-control control" style="min-width: 300px;"> <select id="product-selector" name="product_id" title="<?= __('Choose product') ?>" class="admin__control-text required-entry _required admin__control-text"> <?php foreach ($block->getProductsList() as $productId => $product): ?> <option value="<?= (int)$productId ?>" data-have-options="<?= (int)$product['have_options']?>" ><?= $block->escapeHtml($product['label']) ?></option> <?php endforeach ?> </select> </div> </div> <div class="admin__field field admin__field-small required _required"> <label class="label admin__field-label" for="options-selector"><span><?= __('Select options') ?></span></label> <div id="options-area" class="admin__field-control control"></div> </div> <div class="admin__field field admin__field-small required _required"> <label class="label admin__field-label" for="qty"><span><?= __('Input quantity') ?></span></label> <div id="attributes" class="admin__field-control control" style="min-width: 300px;"> <input type="text" id=qty" name="qty" value="1" /> </div> </div> <div class="admin__field field admin__field-small required _required"> <div class="admin__field-control control" style="margin-left: 800px;"> <input name="form_key" type="hidden" value="<?= /* @escapeNotVerified */ $block->getFormKey() ?>" /> <button type="submit" class="action-default primary" name="submit" value="add"><?= __("Submit") ?></button> </div> </div> </fieldset> </form> <script> require([ 'jquery', ], function ($) { $('#product-selector').change(function () { var productId = $(this).val(); var $optionsArea = $('#options-area'); var optionsUrl = '<?= $block->getOptionsUrl() ?>'; $optionsArea.closest('.admin__field').css({display: 'none'}); $optionsArea.html(''); if ($(this).find('[value="' + productId + '"]').data('have-options')) { $.ajax({ url: optionsUrl, type: 'POST', dataType: 'JSON', data: { form_key: '<?= $block->getFormKey() ?>', product_id: productId, }, success: function (response) { if (response.success && Object.keys(response.data.sku).length > 0) { $selector = $('<select name="sku" class="admin__control-text required-entry _required admin__control-text">') .attr('name','sku'); response.data.sku.forEach(function (sku) { $selector.append($('<option>').val(sku).text(sku)); }); $optionsArea.append( $('<div class="admin__field-control control">').append($selector) ); $optionsArea.closest('.admin__field').css({display: 'block'}); } } }); } }).trigger('change'); }); </script> |
Summary
This is how you solve the problem with adding new product programmatically to existing Cart in Magento 2. If you have any questions or comments, feel free to leave them in the comments.
Magento Custom Development
Take your online store to the next level with BelVG Magento Custom Development
Visit the page
Hello,
A great guide. Yes, this is one of the common issues that occur when products added to the cart pragmatically.
Thanks for providing a comprehensive solution!