I am struggling with Bundle Product add to cart programmatically in Magento 2.
Here I need to add all products with custom price and quantity and I want to do this operation in Product list.phtml
file.
Can you help me with this, please?
I am struggling with Bundle Product add to cart programmatically in Magento 2.
Here I need to add all products with custom price and quantity and I want to do this operation in Product list.phtml
file.
Can you help me with this, please?
Create a Controller
<?php
namespace Company\Module\Controller\Index;
use Magento\Catalog\Model\Product;
use Magento\Catalog\Model\ProductFactory;
use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
use Magento\Framework\Controller\Result\Json as ResultJson;
use Magento\Framework\Controller\Result\JsonFactory;
use Magento\Framework\Data\Form\FormKey;
use Magento\Checkout\Model\Cart;
class Addtocart extends Action
{
/**
* @var ProductFactory
*/
private $productFactory;
/**
* @var Cart
*/
private $cart;
/**
* @var FormKey
*/
private $formKey;
/**
* @var JsonFactory
*/
private $resultJsonFactory;
/**
* @param Context $context
* @param FormKey $formKey
* @param JsonFactory $resultJsonFactory
* @param Cart $cart
* @param ProductFactory $productFactory
*/
public function __construct(
Context $context,
FormKey $formKey,
JsonFactory $resultJsonFactory,
Cart $cart,
ProductFactory $productFactory
) {
parent::__construct($context);
$this->formKey = $formKey;
$this->productFactory = $productFactory;
$this->resultJsonFactory = $resultJsonFactory;
$this->cart = $cart;
}
/**
*
* @return ResultJson
*/
public function execute()
{
$data = $this->getRequest()->getParams();
$resultData = [
'status' => 'success',
'status_code' => 1,
'message' => __('Products Added successfully.')
];
$result = $this->resultJsonFactory->create();
$qty = 1;
$product = $this->productFactory->create()->load($productId);
$productsArray = $this->getBundleOptions($product);
$params = [
'product' => $productId,
'bundle_option' => $productsArray,
'qty' => $qty
];
try {
/**
* Add bundle product in cart
*/
if ($product->getId()) {
$this->cart->addProduct($product, $params);
$this->cart->save();
}
} catch (\Exception $e) {
$resultData = [
'status' => 'fail',
'status_code' => 0,
'message' => __('Unable to add the Product. Exception ' . $e->getMessage())
];
return $result->setData($resultData);
}
return $result->setData($resultData);
}
/**
* get all the selection products used in bundle product
* @param $product
* @return mixed
*/
private function getBundleOptions(Product $product)
{
$selectionCollection = $product->getTypeInstance()
->getSelectionsCollection(
$product->getTypeInstance()->getOptionsIds($product),
$product
);
$bundleOptions = [];
foreach ($selectionCollection as $selection) {
$bundleOptions[$selection->getOptionId()][] = $selection->getSelectionId();
}
return $bundleOptions;
}
}
This worked for me (Magento 2.2.6):
$product = $this->_productRepository->getById($id, false, null, true);
$this->_cart->addProduct($product, $data);
where $data
is an array as shown below:
$data = [
'qty' => 1,
'product' => 45,
'bundle_option' => [
1 => 4,
2 => 8,
3 => 2,
4 => 7,
],
'bundle_option_qty' => [
1 => 1,
2 => 4,
3 => 2,
4 => 14,
]
];
the keys inside bundle_option
and bundle_option_qty
are indexes, not some ID's. I reckon it doesn't matter what these keys are as long as they correspond to the keys in the other array.
Items inside bundle_option
array are options ID's and the other contains quantities so in this case we add 1 item ID=4, 4 items ID=8 and so on...
I'm a bit late to the party, but I was struggling with the same issue myself. There were a few bundle products with required attributes. In order to tackle this in my case, a seperate product attribute called "is_fixed_bundle" was created, and the below code was used to be able to add the product to cart including its required options from the catalog list, based on some of the answers used in this question. So, for people reading this and those who are working on Magento 2.3+, this might help.
app/code/Vendor/Module/etc/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\Catalog\Model\Product\Type\AbstractType">
<plugin disabled="false" name="Vendor_Module_Plugin_Magento_Catalog_Model_Product_Type_AbstractType" sortOrder="10" type="Vendor\Module\Plugin\Magento\Catalog\Model\Product\Type\AbstractType"/>
</type>
<type name="Magento\Checkout\Controller\Cart\Add">
<plugin disabled="false" name="Vendor_Module_Plugin_Magento_Checkout_Controller_Cart_Add" sortOrder="10" type="Vendor\Module\Plugin\Magento\Checkout\Controller\Cart\Add"/>
</type>
</config>
Create the plugins:
app/code/Vendor/Module/Plugin/Magento/Catalog/Model/Product/Type/AbstractType.php
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Vendor\Module\Plugin\Magento\Catalog\Model\Product\Type;
class AbstractType
{
/**
* There are some bundle products with attribute "is_fixed_bundle" indicating this product
* contains required products in its bundle, magento by default does not allow adding these bundles
* to cart without specifying the bundle products.
* @param \Magento\Catalog\Model\Product\Type\AbstractType $subject
* @param $result
* @param $product
* @return bool
*/
public function afterIsPossibleBuyFromList(
\Magento\Catalog\Model\Product\Type\AbstractType $subject,
$result,
$product
) {
// check if result initially is false, because we only want to double-check then
// please note this needs to have product attribute "is_fixed_bundle"
if (!$result && $product->hasIsFixedBundle() && (bool) $product->getIsFixedBundle()) {
return true;
}
return $result;
}
}
Next is the Add plugin, which is a beforePlugin on the add to cart route:
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Vendor\Module\Plugin\Magento\Checkout\Controller\Cart;
use Magento\Catalog\Model\{ProductFactory, Product};
class Add
{
/**
* @var ProductFactory
*/
private $productFactory;
/**
* @param ProductFactory $productFactory
*/
public function __construct(
ProductFactory $productFactory
)
{
$this->productFactory = $productFactory;
}
/**
* @param \Magento\Checkout\Controller\Cart\Add $subject
* @return array
*/
public function beforeExecute(
\Magento\Checkout\Controller\Cart\Add $subject
) {
$requestParams = $subject->getRequest()->getParams();
// we already have bundle option, meaning we're on product detail page, skip further processing of bundle options
if (isset($requestParams['bundle_option'])) {
return [];
}
$productId = (int)$subject->getRequest()->getParam('product');
$product = $this->productFactory->create()->load($productId);
if ($product && $product->hasIsFixedBundle() && (bool) $product->getIsFixedBundle()) {
$bundleOptions['bundle_option'] = $this->getBundleOptions($product);
$subject->getRequest()->setParams(array_merge($requestParams, $bundleOptions));
}
return [];
}
/**
* get all the selection products used in bundle product
* @param $product
* @return mixed
*/
private function getBundleOptions(Product $product)
{
$selectionCollection = $product->getTypeInstance()
->getSelectionsCollection(
$product->getTypeInstance()->getOptionsIds($product),
$product
);
$bundleOptions = [];
foreach ($selectionCollection as $selection) {
$bundleOptions[$selection->getOptionId()][$selection->getProductId()] = $selection->getSelectionId();
}
return $bundleOptions;
}
}
A thing to note, the usage of ->load()
is deprecated and should be replaced by using the product resource model.
Parts of this module was created using mage2gen, thanks to @Devendra Gupta for the getBundleOptions() method.
Hi you can try like below.
$product = $this->productRepository->getById("29");
$magento = array(21=>"1",22 => "2", 23 => "3");
$par = array('product' => $product->getId(),
'bundle_options' => array( 1 => $magento),
'qty' => $qty);
$this->cart->addProduct($_product,$par);
$this->cart->save();
$magento
parameters 21,22,23 are child product ids
$par
should be below screenshot.
And finally Bundle product should configure the like below screenshot other wise it won't work.
Now run the above code it will add the bundle product to cart.
Feel free to ask if you have any queries.
I have implemented in the following way,
i have add to cart url like this:
$cartHelper = $this->helper('Magento\Checkout\Helper\Cart');
$addToCartUrl = $cartHelper->getAddUrl($product);
If the the product type is bundle, then get the bundle options as magento requires to add it to cart.
$productBundleOptions = $product->getProductOptions()['info_buyRequest'];
then add a form with hidden values of bundle options in phtml file like this :
<form data-role="tocart-form" action="<?php echo $addToCartUrl; ?>" method="post">
<?php echo $block->getBlockHtml('formkey')?>
<div class="btn">
<button type="submit" title="Add to Cart" class="action tocart primary">
<span>Add to Cart</span>
</button>
</div>
<?php
$bundleOptionArray = $productBundleOptions['bundle_option'];
foreach ($bundleOptionArray as $bundleOptionArrayKey => $bundleOptionArrayValue) { ?>
<input type="hidden" class="bundle-option-<?= $bundleOptionArrayKey ?> product bundle option" name="bundle_option[<?= $bundleOptionArrayKey ?>]" value="<?= $bundleOptionArrayValue ?>" id="bundle-option-<?= $bundleOptionArrayKey."-".$bundleOptionArrayValue ?>" checked="checked" aria-required="true">
<?php } ?>
</form>
Please find below code I have used for add to cart bundle product programmatically.
The $this->_product
property is an object of \Magento\Catalog\Model\Product
The $this->cartManagementInterface
property is an object of \Magento\Quote\Api\CartManagementInterface
The $this->cartRepositoryInterface
property is an object of \Magento\Quote\Api\CartRepositoryInterface
$cartId = $this->cartManagementInterface->createEmptyCart(); //Create empty cart
$quote = $this->cartRepositoryInterface->get($cartId);
$qty = 2;
$items = $orderData->getAllItems();
foreach ($items as $innerItem) {
if ($innerItem->getProductType() == 'bundle' ){
$product = $this->_product->load($innerItem->getProductId());
$buyRequest = $innerItem->getProductOptionByCode('info_buyRequest');
$buyRequest = new \Magento\Framework\DataObject($buyRequest);
$buyRequest->setQty($qty);
$quote->addProduct($product, $buyRequest);
}
}