How to create magento helper class? - php

I have the following magento helper class.
class CommissionJunction extends Mage_Core_Helper_Data
{
/**
* Get SKU, quantity, price and discount amount for each product in a given order
* #param object $order
* #return array
*/
private function _getOrderProductsList($order) {
$orderItems = $order->getAllItems();
$purchasedSkus = array();
$count_orderItems = count($orderItems);
for($i = 0; $i < $count_orderItems; $i++) {
$purchasedSkus[$i] = array(
'ITEM' => $orderItems[$i]['sku'],
'QTY' => number_format($orderItems[$i]['qty_ordered'],0), // no decimals
'AMT' => number_format($orderItems[$i]['price'],2) // 2 decimal places
'DCNT' => number_format(abs($orderItems[$i]['discount_amount']),2) */
);
}
return $purchasedSkus;
}
/**
* Get the Universal Data (JSON) Object for Commission Junction.
* This object contains the order details passed on to Commission Junction for reporting purposes
* on the Checkout Success / Order Confirmation page.
* Notes:
* - CID, TYPE AND CURRENCY are hard coded
* #param string $orderId
* #return JSON object Universal Data Object for Commission Junction $json_masterTmsUdp
*/
public function getCommissionJunctionUdo($orderId) {
$order = Mage::getModel('sales/order')->loadByIncrementId($orderId);
$udo = array();
$udo['CID'] = 'XXXX';
$udo['TYPE'] = 'XXXX';
$udo['CURRENCY'] = 'USD';
$udo['OID'] = $orderId;
$udo['DISCOUNT'] = number_format(abs($order->discount_amount),2);
$order_coupon_code = $order->coupon_code;
if(!is_null($order_coupon_code) && !empty($order_coupon_code)) {
$udo['COUPON'] = $order_coupon_code;
}
$udo['PRODUCTLIST'] = self::_getOrderProductsList($order);
if(Mage::getModel('core/cookie')->get('aff_commissionjunction') == 'cjafflx') {
$udo['FIRECJ'] = "TRUE";
}
else {
$udo['FIRECJ'] = "FALSE";
}
$masterTmsUdo['CJ'] = $udo;
$json_masterTmsUdo = json_encode($masterTmsUdo);
return $json_masterTmsUdo;
}
}
And I found this site where they explain how to register a helper class
http://codegento.com/2011/02/creating-a-helper-class/
The only thing I dont know its:
Where in the magento structure should I add this php class?
Where is the config.xml I should edit?

User created classes should be placed in this folder:
app/code/community
Another issue, you should use the same naming convention for your class as Magento uses i.e.
MOduleNameSpace_ComissionJunction_Helper_Data
Also the config XML should be placed in your app/code/community/ MOduleNameSpace/ComissionJunction/etc folder

Related

Why are final prices missing on larger product exports?

The requirement is to fix the final price export in Magento 2.4.2. The issue is as follows:
We want to export product data into a CSV. We also want to export the final prices of the products. For that, an after Plugin is supposed to take the exported array and add an additional column for the final price before it gets handed to the file writing logic of the export.
The Plugin works as follows:
use Magento\Catalog\Api\Data\ProductExtensionInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Model\Product\Type\Price;
use Magento\Catalog\Model\Product;
use Magento\Catalog\Model\ResourceModel\Product as ResourceModel;
use Magento\Framework\Exception\NoSuchEntityException;
use Firebear\ImportExport\Model\Export\Product as FirebearProduct;
class AfterGetExportDataPlugin
{
public const FINAL_PRICE_EXPORT = 'export_finalPrice';
private ResourceModel $resourceModel;
private ProductRepositoryInterface $productRepository;
private Price $price;
public function __construct(
ResourceModel $resourceModel,
ProductRepositoryInterface $productRepository,
Price $price
) {
$this->resourceModel = $resourceModel;
$this->productRepository = $productRepository;
$this->price = $price;
}
/**
* #param FirebearProduct $subject
* #param array $result
* #return array
* #throws NoSuchEntityException
*/
public function afterGetExportData(FirebearProduct $subject, array $result)
{
// Evaluate if final price is a selected attribute for the export
$additionalColumns = $subject->getParameters()['list'];
if (false === in_array(self::FINAL_PRICE_EXPORT, $additionalColumns, true)) {
return $result;
}
// Evaluate if an alias for final price is set
$exportAttributeMapping = self::FINAL_PRICE_EXPORT;
for ($i = 0; $i < $additionalColumns; $i++) {
if ($additionalColumns[$i] === self::FINAL_PRICE_EXPORT) {
$exportAttributeMapping = $subject->getParameters()['replace_code'][$i];
break 1;
}
}
return array_map(function ($product) use ($exportAttributeMapping) {
/** #var Product $productEntity */
$productEntity = $this->productRepository->get((string) $product['sku']);
$product[$exportAttributeMapping] = (float) $this->price->getFinalPrice(1, $productEntity);
return $product;
}, $result);
}
}
I tested that with 5000 products on my development environment and the export went well. But when we exported 400000 products on our staging environment, the majority of final prices were missing.
I can't figure out why that is. Does anyone how or why that happens?
FYI: We use the Firebear export extension but I don't think it matters that much in this context.

PayPal IPN for disputes/chargebacks

So I am working with the PayPal IPN for a payment gateway in my store project. Users can create stores and add a paypal gateway which is just storing their PayPal email.
How can I listen for chargebacks on that payment so I can then act upon that. Please note that I obviously do not have access to these paypal accounts, so I cannot setup a listener in their paypal.
When I create a payment I am building a query and then sending them to that URL, in that query is the notify_url, will that be used again if someone were to create a dispute on the payment. I tried to create a dispute however nothing seemed to happen to my database (What the notify url does).
Any help would be great.
Here is a the codeigniter library I am using:
[CODE]
class Paypal {
var $config = Array();
var $production_url = 'https://www.paypal.com/cgi-bin/webscr?';
var $sandbox_url = 'https://www.sandbox.paypal.com/cgi-bin/webscr?';
var $item = 1;
/**
* Constructor
*
* #param string
* #return void
*/
public function __construct($props = array())
{
$this->__initialize($props);
log_message('debug', "Paypal Class Initialized");
}
// --------------------------------------------------------------------
/**
* initialize Paypal preferences
*
* #access public
* #param array
* #return bool
*/
function __initialize($props = array())
{
$config["business"] = ''; //Your PayPal account
$config["cmd"] = '_cart'; //Do not modify
$config["production"] = FALSE;
#Custom variable here we send the billing code-->
$config["custom"] = '';
$config["invoice"] = '';
#API Configuration-->
$config["upload"] = '1'; //Do not modify
$config["currency_code"] = 'GBP';
$config["disp_tot"] = 'Y';
#Page Layout -->
$config["cpp_header_image"] = ''; //Image header url [750 pixels wide by 90 pixels high]
$config["cpp_cart_border_color"] = '000'; //The HTML hex code for your principal identifying color
$config["no_note"] = 1; //[0,1] 0 show, 1 hide
#Payment Page Information -->
$config["return"] = ''; //The URL to which PayPal redirects buyers’ browser after they complete their payments.
$config["cancel_return"] = ''; //Specify a URL on your website that displays a “Payment Canceled” page.
$config["notify_url"] = ''; //The URL to which PayPal posts information about the payment
$config["rm"] = '2'; //Leave this to get payment information
$config["lc"] = 'EN'; //Languaje [EN,ES]
#Shipping and Misc Information -->
$config["shipping"] = '';
$config["shipping2"] = '';
$config["handling"] = '';
$config["tax"] = '';
$config["discount_amount_cart"] = ''; //Discount amount [9.99]
$config["discount_rate_cart"] = ''; //Discount percentage [15]
#Customer Information -->
$config["first_name"] = '';
$config["last_name"] = '';
$config["address1"] = '';
$config["address2"] = '';
$config["city"] = '';
$config["state"] = '';
$config["zip"] = '';
$config["email"] = '';
$config["night_phone_a"] = '';
$config["night_phone_b"] = '';
$config["night_phone_c"] = '';
/*
* Convert array elements into class variables
*/
if (count($props) > 0)
{
foreach ($props as $key => $val)
{
$config[$key] = $val;
}
}
$this->config = $config;
}
// --------------------------------------------------------------------
/**
* Perform payment process
*
* #access public
* #param array
* #return void
*/
function pay(){
#Convert the array to url encode variables
$vars = http_build_query($this->config);
if($this->config['production'] == TRUE){
header('LOCATION:'.$this->production_url.$vars);
}else{
header('LOCATION:'.$this->sandbox_url.$vars);
}
}
// --------------------------------------------------------------------
/**
* Add a product to the list
*
* #access public
* #param array
* #return void
*/
function add($item_name = '',$item_amount = NULL,$item_qty = NULL,$item_number = NULL){
$this->config['item_name_'.$this->item] = $item_name;
$this->config['amount_'.$this->item] = $item_amount;
$this->config['quantity_'.$this->item] = $item_qty;
$this->config['item_number_'.$this->item] = $item_number;
$this->item++;
}
}
// END Paypal Class
/* End of file Paypal.php /
/ Location: ./application/libraries/Paypal.php */
[/CODE]

Magento2 addFieldToFilter call works with hardcoded value but not variable with same value

I'm building the admin for a Magento2 store (currently on 2.1.7, they want to use the newest version until we go live and then want to stabilize a particular version). The module in question is supposed to display all existing orders, with an actionsColumn that contains links to cancel, edit, and open a detailed overview of the purchased items associated with that order. The order detail page contains a grid view that should display all order items associated with an order number passed in the URL.
In order to filter out Order Items that don't relate to the specific Order Number, I've extended the \Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult class. This works except for one weird caveat. If, in the addFieldToFilter call, I replace $ordNum with, say, '10000', it grabs the correct data. When using $ordNum to call this dynamically, however, it returns no rows at all. This despite trying all sorts of casting and === checks to ensure that there's no difference between the hardcoded and dynamic values. Is this a Magento bug? I can't at all figure out why this would be the case.
<?php
class OrderItems extends \Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult
{
protected function _initSelect()
{
$this->filterByOrderNum();
parent::_initSelect();
return $this;
}
private function filterByOrderNum()
{
$request = \Magento\Framework\App\ObjectManager::getInstance()
->get('Magento\Framework\App\Request\Http');
$ordNum = $request->getParam('order_num');
$this->addFieldToFilter('order_num', ['eq' => $ordNum]); //if I switch this to hardcoded 10000, this works. With the variable, no dice.
return $this;
}
}
I just fixed it by using mentioned below steps
Store param value in session in controller
public function execute() {
$this->_catalogSession->setTokenId($this->request->getParam('entity_id'));
$this->_view->loadLayout();
$this->_view->loadLayoutUpdates();
$this->_view->getPage()->getConfig()->getTitle()->set(__('Redeem Token History'));
$this->_view->renderLayout();
}
Use session value in dataprovider
$tokensCollection->addFieldToFilter('token_id', ['eq' => $this->_catalogSession->getTokenId()]);
Enjoy :)
Try this in place of the getParam statement:
$url = parse_url($request);
$path = explode('/',$url['path']);
$ordNum = $path[3];
Just to make sure we are on the same page, this is the full code:
<?php
class OrderItems extends \Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult
{
protected function _initSelect()
{
$this->filterByOrderNum();
parent::_initSelect();
return $this;
}
private function filterByOrderNum()
{
$request = \Magento\Framework\App\ObjectManager::getInstance()
->get('Magento\Framework\App\Request\Http');
$url = parse_url($request);
$path = explode('/',$url['path']);
$ordNum = $path[3];
$this->addFieldToFilter('order_num', $ordNum); //if I switch this to hardcoded 10000, this works. With the variable, no dice.
return $this;
}
}
We have solved this issue by doing the following :
/**
* CcCustompriceProductListingDataProvider constructor.
* #param string $name
* #param string $primaryFieldName
* #param string $requestFieldName
* #param \Magento\Framework\Api\Search\ReportingInterface $reporting
* #param \Magento\Framework\Api\Search\SearchCriteriaBuilder $searchCriteriaBuilder
* #param \Magento\Framework\App\RequestInterface $request
* #param \Magento\Framework\Api\FilterBuilder $filterBuilder
* #param array $meta
* #param array $data
* #throws \Exception
*/
public function __construct(
$name,
$primaryFieldName,
$requestFieldName,
ReportingInterface $reporting,
SearchCriteriaBuilder $searchCriteriaBuilder,
RequestInterface $request,
FilterBuilder $filterBuilder,
array $meta = [],
array $data = []
) {
$data['config']['filter_url_params']['product_id'] = $request->getParam('cppc_product_id', 0);
parent::__construct($name, $primaryFieldName, $requestFieldName, $reporting, $searchCriteriaBuilder, $request, $filterBuilder, $meta, $data);
}
You do not need to use any other function. The reason why this is is because it is also updated with an update URL and that does not have that parameter. By using adding that to the data it also parses that into the update url.
You can see that here (Parent function)
/**
* #return void
*/
protected function prepareUpdateUrl()
{
if (!isset($this->data['config']['filter_url_params'])) {
return;
}
foreach ($this->data['config']['filter_url_params'] as $paramName => $paramValue) {
if ('*' == $paramValue) {
$paramValue = $this->request->getParam($paramName);
}
if ($paramValue) {
$this->data['config']['update_url'] = sprintf(
'%s%s/%s/',
$this->data['config']['update_url'],
$paramName,
$paramValue
);
$this->addFilter(
$this->filterBuilder->setField($paramName)->setValue($paramValue)->setConditionType('eq')->create()
);
}
}
}

Using Raw SQL in SuiteCRM

So, this is my first encounter with SuiteCRM or any other CRM for that matter. I need to query the db on a table that is not used by the CRM for our quote system. So, I have created the module using module builder and modified the module file so that it uses the correct table. The problem is that when the query runs, SuiteCRM still adds its default where clauses and adds the deleted = 0 condition to the query.
So, I tried using the method that is described on this SO page. That doesn't work as I get the an error that I am using an undefined variable (db) and that I am calling to a member function fetchByAssoc() on a non-object. Now, I am placing the code in the moduleName.php file. Maybe that is my issue. I don't know as I have never worked on any other CRM project. If anyone can point me in the right direction as to what I will need to do to be able to query a different table other than the default CRM table and then show the results from that query inside of a dashlet, your help will be greatly appreciated.
I got the errors fixed. They were my fault as I had not referenced the object.
So, as requested, here is some of my code. This is my php file.
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
require_once('include/Dashlets/Dashlet.php');
class FrtwQuotesDashlet extends Dashlet {
var $height = '200'; // height of the dashlet
var $quoteData = "";
/**
* Constructor
*
* #global string current language
* #param guid $id id for the current dashlet (assigned from Home module)
* #param array $def options saved for this dashlet
*/
function FrtwQuotesDashlet($id, $def) {
$this->loadLanguage('FrtwQuotesDashlet');
if(!empty($def['height'])) // set a default height if none is set
$this->height = $def['height'];
parent::Dashlet($id); // call parent constructor
$this->isConfigurable = true; // dashlet is configurable
$this->hasScript = false; // dashlet has javascript attached to it
// if no custom title, use default
if(empty($def['title'])) $this->title = $this->dashletStrings['LBL_TITLE'];
else $this->title = $def['title'];
}
/**
* Displays the dashlet
*
* #return string html to display dashlet
*/
function display() {
$sql = "SELECT QuoteNbr, crmname, ShipToCity FROM quotes.quotehdr LIMIT 10";
$result = $GLOBALS["db"]->query($sql);
$quoteData = "<table>";
while($quotes = $GLOBALS["db"]->fetchByAssoc($result)){
foreach ($quotes as $quote) {
$quoteData .="<tr><td>".$quote[0]."</td><td>".$quote[1]."</td><td>".$quote[2]."</td></tr>";
}
}
$ss = new Sugar_Smarty();
//assign variables
//$ss->assign('greeting', $this->dashletStrings['LBL_GREETING']);
$ss->assign('quoteData', $this->quoteData);
$ss->assign('height', $this->height);
$str = $ss->fetch('custom/modules/Home/FrtwQuotesDashlet/FrtwQuotesDashlet.tpl');
return parent::display().$str;
}
}
?>
The issue was with the foreach loop. I removed it and now it works fine. In analyzing the code, the while loop actually does the iterations needed. So, by adding the foreach, what was happening was that the code was iterating over each row returned from the db and then doing some weird stuff -- as in, it would only return a partial string of what each value should be. Since I am querying on 3 fields, it would also loop over each row 3 times, thereby creating 3 different rows from each row. So, for anyone with similar issue, this is how working code looks.
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
require_once('include/Dashlets/Dashlet.php');
class FrtwQuotesDashlet extends Dashlet {
var $height = '200'; // height of the dashlet
var $quoteData = "";
/**
* Constructor
*
* #global string current language
* #param guid $id id for the current dashlet (assigned from Home module)
* #param array $def options saved for this dashlet
*/
function FrtwQuotesDashlet($id, $def) {
$this->loadLanguage('FrtwQuotesDashlet');
if(!empty($def['height']))
$this->height = $def['height'];
parent::Dashlet($id);
$this->isConfigurable = true;
$this->hasScript = false;
// if no custom title, use default
if(empty($def['title'])) $this->title = $this->dashletStrings['LBL_TITLE'];
else $this->title = $def['title'];
}
/**
* Displays the dashlet
*
* #return string html to display dashlet
*/
function display() {
$sql = "SELECT QuoteNbr, revnbr, crmname, ShipToCity FROM quotes.quotehdr LIMIT 10";
$result = $GLOBALS["db"]->query($sql);
$this->quoteData = "Need headers here when we determine exact fields....";
while($quotes = $GLOBALS["db"]->fetchByAssoc($result)){
$this->quoteData .="<tr><td width = \"30%\">".$quotes["QuoteNbr"].' '.$quotes['revnbr']."</td><td width = \"30%\">".$quotes["crmname"]."</td><td width = \"30%\">".$quotes["ShipToCity"]."</td></tr>";
}
$ss = new Sugar_Smarty();
//assign variables
// $ss->assign('greeting', $this->dashletStrings['LBL_GREETING']);
$ss->assign('greeting', "This is the Greeting....");
$ss->assign('quoteData', $this->quoteData);
$ss->assign('height', $this->height);
$str = $ss->fetch('modules/Home/Dashlets/FrtwQuotesDashlet/FrtwQuotesDashlet.tpl');
return parent::display().$str; // return parent::display for title and such
}
}
?>

Magento: Adding simple products from a bundle to separate lines in the cart

My client is requesting that each simple product within a bundled product they are selling (Clothing Top and Bottom) be added as a separate line item in the cart whenever a user adds it. Can anyone direct me in how to accomplish this? I am fairly good with MVC and the Zend Framework, but I need a bit of help finding the exact files that control adding bundled products to the cart, or an alternate method for getting these items added separately. Please assume that the only possible product type for this clothing is the Bundled product type.
You will need an observer:
<checkout_cart_product_add_after>
<observers>
<reporting>
<type>singleton</type>
<class>Yourcompany_yourmodelname_Model_Observer</class>
<method>onCartProductAdd</method>
</reporting>
</observers>
</checkout_cart_product_add_after>
Then do the observer:
class ourcompany_yourmodelname_Model_Observer extends Mage_Core_Model_Abstract
{
/**
* Binds to the checkout_cart_product_add_after Event and passes control to the helper function to process the quote
*
* #param Varien_Event_Observer $observer
* #return void
*/
public function onCartProductAdd($observer){
$product = $observer->getProduct();
$isProductBundle = ($product->getTypeId() == 'bundle');
$items_to_add = array();
$oTypeInstance = $oProduct->getTypeInstance(true);
$aSelections = $oTypeInstance->getSelectionsCollection($aOptionIds, $product );
$aOptions = $oTypeInstance->getOptionsByIds($aOptionIds, $product);
$bundleOptions = $aOptions->appendSelections($aSelections, true);
foreach ($bundleOptions as $bundleOption) {
if ($bundleOption->getSelections()) {
$bundleSelections = $bundleOption->getSelections();
foreach ($bundleSelections as $bundleSelection) {
$items_to_add[] = $bundleSelection.getID();
}
}
}
insertExtractedProducts($items_to_add);
}
/**
* Add extracted products into quote
*
* #param array $items_to_add
*/
public function insertExtractedProducts($items_to_add){
/**#var $cart Mage_Checkout_Model_Cart**/
$cart = Mage::helper('checkout/cart')->getCart();
$ids_to_add = array();
foreach($items_to_add as $item_to_be_added){
$ids_to_add[] = $item_to_be_added->getProductId();
}
$cart->addProductsByIDs($ids_to_add);
$cart->save();
Mage::getSingleton('checkout/session')->setCartWasUpdated(true);
}
}
Just a simple sample, but it might help.
Bundled products can be complicated to understand, when working with it via code:
Here is a sample image:
Each Bundled product, have one to many options, which in the end will be links to products to be added to the bundle in the Shopping Cart.
Each Option consists of one to many Selections, which will be the linked products that will end up in the Shopping cart, under this bundled product. One Selection, can typically be set as the default, and will already be selected on the product page. More information can be found at this link on how to create and configure bundled products, because in this document, we will only discuss the programming side of it.
The bundled product’s display page, will look like this:
It could look like this in the shopping cart, once you clicked on “Add to Cart”:
Bundle Sample
Sample Shampoo
1 x Moisturiser-125ml $29.95
Default Conditioner
1 x Moisturiser-60g $99.95
When interrogating this product through code, you load it like any normal product:
$oProduct->load($vProductId);
Once it is loaded, you need to get a product type instance, so that you can load the options for this product type.
$oTypeInstance = $oProduct->getTypeInstance(true);
Now we can get the list of option ID’s for this product in this manner:
$oTypeInstance = $oProduct->getTypeInstance(true);
To interrogate the Selections, we will add the list of options to a collection, then get the Options collection, as well as their respective Selections:
$aSelections = $oTypeInstance->getSelectionsCollection($aOptionIds, $oProduct );
$aOptions = $oTypeInstance->getOptionsByIds($aOptionIds, $oProduct);
$bundleOptions = $aOptions->appendSelections($aSelections, true);
Now we have a Collection of Options, and each Option will have a collection of Selections. We can now loop through the options, and look at their Selctions.
foreach ($bundleOptions as $bundleOption) {
if ($bundleOption->getSelections()) {
$bundleSelections = $bundleOption->getSelections();
foreach ($bundleSelections as $bundleSelection) {
// get some data here
$vName = $bundleOption->getTitle();
}
}
}
To get a list of the Required Selection Products for the Bundled product, you can use the following code:
$requiredChildren = $this->getChildrenIds($product->getId(),$required=true);
You can then loop through the array of ID’s and load the products by their ID’s to get more information regarding those products.
Bundled products in the shopping cart
In order to loop through the selected options of a Bundled product in the shopping card, you can use something like this:
/**
* #var Mage_Bundle_Model_Product_Type
*/
$typeInstance = $this->getProduct()->getTypeInstance(true);
// get bundle options
$optionsQuoteItemOption = $this->getItem()->getOptionByCode('bundle_option_ids');
$bundleOptionsIds = unserialize($optionsQuoteItemOption->getValue());
if ($bundleOptionsIds) {
/**
* #var Mage_Bundle_Model_Mysql4_Option_Collection
*/
$optionsCollection = $typeInstance->getOptionsByIds($bundleOptionsIds, $this->getProduct());
// get and add bundle selections collection
$selectionsQuoteItemOption = $this->getItem()->getOptionByCode('bundle_selection_ids');
$selectionsCollection = $typeInstance->getSelectionsByIds(
unserialize($selectionsQuoteItemOption->getValue()),
$this->getProduct()
);
$bundleOptions = $optionsCollection->appendSelections($selectionsCollection, true);
foreach ($bundleOptions as $bundleOption) {
if ($bundleOption->getSelections()) {
$label = $bundleOption->getTitle()
$bundleSelections = $bundleOption->getSelections();
foreach ($bundleSelections as $bundleSelection) {
$sName = $bundleSelection->getName();
}
// some more code here to do stuff
}
}
}
This code gets the Options from the Quote Item Bundled Product, and then gets the Options for that product in a collection, and then finds the “Selected” Option Selection.
hth,
Shaun
Here's how you can do this in Magento 2.3 via an "around" plugin (interceptor) for the Magento\Checkout\Model\Cart->addProduct() method.
The addProduct() method is called when the customer adds a product to the cart. By adding code to this method via a plugin, you can alter the way the bundle product is added to the cart.
Define the plugin in Vendor/Module/etc/frontend/di.xml:
<?xml version="1.0"?>
<!--
/**
* Copyright © 2016 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<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="add-bundle-products-separate" type="Vendor\Module\Plugin\Checkout\Model\CartPlugin" sortOrder="1"/>
</type>
</config>
This is what tells Magento a plugin exists for this particular method.
Reference:
https://devdocs.magento.com/guides/v2.3/extension-dev-guide/plugins.html
Create the plugin's class.
In Vendor/Module/Plugin/Checkout/Model/CartPlugin.php:
<?php
namespace Vendor\Module\Plugin\Checkout\Model;
use \Magento\Catalog\Model\Product;
use \Magento\Framework\DataObject;
use \Magento\Checkout\Model\Cart;
/**
* Class CartPlugin
*
* #package Ppwd\CrossSell\Plugin\Checkout\Model
*
*/
class CartPlugin {
/**
* #param Cart $subject
* #param \Closure $proceed
* #param Product $productInfo
* #param DataObject|int|array $requestInfo
* #return Cart $subject
* #SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function aroundAddProduct(
$subject,
$proceed,
$productInfo,
$requestInfo = null
) {
// Detect if we are adding a bundle product to cart
if (!is_numeric($productInfo) && $productInfo->getTypeId() == 'bundle') {
$buyRequest = new DataObject($requestInfo);
// List of products selected as part of the bundle
$cartCandidates = $productInfo->getTypeInstance()->prepareForCartAdvanced($buyRequest, $productInfo);
$productIds = [];
// Add each item in bundle as if it were separately added to cart
/** #var Product $cartCandidate */
foreach ($cartCandidates as $cartCandidate) {
if ($cartCandidate->getTypeId() != 'bundle') {
for ($i = 0; $i < $cartCandidate->getCartQty(); $i++) {
$productIds[] = $cartCandidate->getId();
}
}
}
$subject->addProductsByIds($productIds);
return $subject;
}
// Return original result from addProduct() as if plugin didn't exist
$result = $proceed($productInfo, $requestInfo);
return $result;
}
}
When done, if you add the bundle to the cart the line items will appear separately instead of grouped together like a bundle product normally is.
I did this to solve the customer request to add bundle content as separate items in the cart. Just replace
$cart->addProduct($product, $params)
with
if ($product->getTypeId() == 'bundle') {
$request = new Varien_Object($params);
$cartCandidates = $product->getTypeInstance(true)->prepareForCartAdvanced($request, $product, Mage_Catalog_Model_Product_Type_Abstract::PROCESS_MODE_FULL);
$idstoadd = array();
foreach ($cartCandidates as $cartCandidate) {
if ($cartCandidate->getTypeId() == 'simple') {
for ($i = 0; $i < $cartCandidate->getCartQty(); $i++) {
$idstoadd[] = $cartCandidate->getId();
}
}
}
$cart->addProductsByIds($idstoadd);
} else {
$cart->addProduct($product, $params);
}
in the file cartController.

Categories