In this post we will check how to display coupon code, Up sell products data (Up sell products of added cart items [simple product's up sell items] ) and totals information in Minicart of Magento2.
Create folder inside app/code/Magelearn/AdvanceCart
<?php \Magento\Framework\Component\ComponentRegistrar::register( \Magento\Framework\Component\ComponentRegistrar::MODULE, 'Magelearn_AdvanceCart', __DIR__ );
Add composer.json file in it:
{ "name": "magelearn/advance-cart", "description": "Display coupon code, Up sell products and total information in Minicart magento2", "type": "magento2-module", "license": "proprietary", "version": "1.0.0", "authors": [ { "name": "Vijay Rami", "email": "vijaymrami@gmail.com" } ], "minimum-stability": "dev", "require": {}, "autoload": { "files": [ "registration.php" ], "psr-4": { "Magelearn\\AdvanceCart\\": "" } } }
Add etc/module.xml file in it:
<?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="Magelearn_AdvanceCart" setup_version="1.0.0"/> </config>
<?xml version="1.0" encoding="UTF-8"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <tab id="advancecart" translate="label" sortOrder="10" class="advancecart-tab"> <label>Advance Cart</label> </tab> <section id="advancecart_setting" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Settings</label> <tab>advancecart</tab> <resource>Magelearn_AdvanceCart::magelearn_advancecart</resource> <group id="minicart_settings" translate="label" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Minicart Settings</label> <field id="cart_text" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Promotional Text</label> </field> <field id="upsell_title" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Upsell Title</label> </field> <field id="upsell_type" translate="label" sortOrder="3" type="select" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Upsell type</label> <!-- source model which we created for drop down options --> <source_model>Magelearn\AdvanceCart\Model\Config\Source\Upselltype</source_model> <comment>Option Dynamic will show the products from the list of upsell products of the products added to cart. Option Static shows the products from the Sku entered in option 'Product Sku'.</comment> </field> <field id="upsell_limit" translate="label" sortOrder="4" type="select" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Upsell Product Limit</label> <!-- source model which we created for drop down options --> <source_model>Magelearn\AdvanceCart\Model\Config\Source\Upselllimit</source_model> <comment>Set the number for products to show on upsell block.</comment> </field> <field id="upsell_view" translate="label" sortOrder="5" type="select" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Upsell View</label> <!-- source model which we created for drop down options --> <source_model>Magelearn\AdvanceCart\Model\Config\Source\Upsellview</source_model> <comment>Option `Slider` will show the products in a slick slider view. Option `Scroll` shows the products in a scrolling view.</comment> </field> <field id="totals" translate="label" type="select" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Show total summary</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> <field id="totals_title" translate="label" type="text" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Totals summary text</label> <depends> <field id="totals">1</field> </depends> </field> <field id="coupon" translate="label" type="select" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Allow user to apply coupon</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> <field id="upsell_sku" translate="label" type="text" sortOrder="9" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Product Sku</label> <depends> <field id="advancecart_setting/minicart_settings/upsell_type">2</field> </depends> </field> </group> </section> </system> </config>
As per highlighted code above we will add source_model files to provide proper system configurations options.
Create app/code/Magelearn/AdvanceCart/Model/Config/Source/Upselllimit.php file.
<?php namespace Magelearn\AdvanceCart\Model\Config\Source; class Upselllimit implements \Magento\Framework\Option\ArrayInterface { /** * @return array */ public function toOptionArray() { $options = []; foreach (range(1, 10) as $number) { $options[] = ['value' => $number, 'label' => __($number)]; } return $options; } }
Create app/code/Magelearn/AdvanceCart/Model/Config/Source/Upselltype.php file.
<?php namespace Magelearn\AdvanceCart\Model\Config\Source; class Upselltype implements \Magento\Framework\Option\ArrayInterface { /** * @return array */ public function toOptionArray() { return [ ['value' => '1', 'label' => __('Dynamic')], ['value' => '2', 'label' => __('Static')] ]; } }
Create app/code/Magelearn/AdvanceCart/Model/Config/Source/Upsellview.php file.
<?php namespace Magelearn\AdvanceCart\Model\Config\Source; class Upsellview implements \Magento\Framework\Option\ArrayInterface { /** * @return array */ public function toOptionArray() { return [ ['value' => '1', 'label' => __('Slider View')], ['value' => '2', 'label' => __('Scroll View')] ]; } }
And will set these options default values in etc/config.xml file.
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> <default> <advancecart_setting> <minicart_settings> <cart_text>Above $100 to qualify for free shipping!</cart_text> <coupon>1</coupon> <totals>1</totals> <totals_title>Your Total:</totals_title> <upsell_title>Don't forget.</upsell_title> <upsell_type>1</upsell_type> <upsell_limit>3</upsell_limit> <upsell_view>2</upsell_view> </minicart_settings> </advancecart_setting> </default> </config>
For that we will add xml file at app/code/Magelearn/AdvanceCart/view/frontend/layout/catalog_product_view.xml
<?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="product.info.addtocart.additional"> <action method="setTemplate"> <argument name="template" xsi:type="string">Magelearn_AdvanceCart::product/view/addtocart.phtml</argument> </action> </referenceBlock> <referenceBlock name="product.info.addtocart"> <action method="setTemplate"> <argument name="template" xsi:type="string">Magelearn_AdvanceCart::product/view/addtocart.phtml</argument> </action> </referenceBlock> </body> </page>
<?php /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ /** @var $block \Magento\Catalog\Block\Product\View */ ?> <?php $_product = $block->getProduct(); ?> <?php $buttonTitle = __('Add to Cart'); ?> <?php if ($_product->isSaleable()) :?> <div class="box-tocart"> <div class="fieldset"> <?php if ($block->shouldRenderQuantity()) :?> <div class="field qty"> <label class="label" for="qty"><span><?= $block->escapeHtml(__('Qty')) ?></span></label> <div class="control"> <span class="minus"><button type="button" title="<?= $block->escapeHtmlAttr(__('Reduce the quantity')); ?>">-</button></span> <input type="number" name="qty" id="qty" min="0" value="<?= $block->getProductDefaultQty() * 1 ?>" title="<?= $block->escapeHtmlAttr(__('Qty')) ?>" class="input-text qty" data-validate="<?= $block->escapeHtml(json_encode($block->getQuantityValidators())) ?>" /> <span class="plus"><button type="button" title="<?= $block->escapeHtmlAttr(__('Increase the quantity')); ?>"/>+</button></span> <script type="text/javascript"> // This is the javascript codes help us to increase and decrease qty require(['jquery'], function ($) { $('.box-tocart .minus').on('click', function () { var qty = parseInt($('#qty').val()); qty = qty - 1; $('#qty').val(qty).trigger('change'); }); $('.box-tocart .plus').on('click', function () { var qty = parseInt($('#qty').val()); qty = qty + 1; $('#qty').val(qty).trigger('change'); }); $('#qty').on('change', function () { var qty = parseInt($(this).val()); if (qty > 100) { $(this).val('100'); } else if (qty < 1) { $(this).val('1'); } }); }); </script> </div> </div> <?php endif; ?> <div class="actions"> <button type="submit" title="<?= $block->escapeHtmlAttr($buttonTitle) ?>" class="action primary tocart" id="product-addtocart-button" disabled> <span><?= $block->escapeHtml($buttonTitle) ?></span> </button> <?= $block->getChildHtml('', true) ?> </div> </div> </div> <?php endif; ?> <script type="text/x-magento-init"> { "#product_addtocart_form": { "Magento_Catalog/js/validate-product": {} } } </script>
For that we will modify in app/code/Magelearn/AdvanceCart/view/frontend/layout/default.xml file.
you can check the vendor/magento/module-checkout/view/frontend/layout/default.xml for referance.
<?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"> <head> <css src="Magelearn_AdvanceCart::css/minicart.css" order="100"/> <link src="Magelearn_AdvanceCart::js/minicart-data.js"/> </head> <body> <referenceContainer name="content"> <block class="Magento\Framework\View\Element\Template" name="minicart.autoopen" template="Magelearn_AdvanceCart::minicart_open.phtml"/> </referenceContainer> <referenceBlock name="minicart"> <arguments> <argument name="jsLayout" xsi:type="array"> <item name="types" xsi:type="array"/> <item name="components" xsi:type="array"> <item name="minicart_content" xsi:type="array"> <item name="component" xsi:type="string">Magento_Checkout/js/view/minicart</item> <item name="config" xsi:type="array"> <item name="template" xsi:type="string">Magelearn_AdvanceCart/minicart/content</item> </item> <item name="children" xsi:type="array"> <item name="promotion" xsi:type="array"> <item name="component" xsi:type="string">Magento_Checkout/js/view/checkout/minicart/subtotal/totals</item> <item name="config" xsi:type="array"> <item name="template" xsi:type="string">Magelearn_AdvanceCart/minicart/total/grand-total</item> </item> </item> </item> </item> <item name="coupon" xsi:type="array"> <item name="component" xsi:type="string">Magelearn_AdvanceCart/js/coupon</item> <item name="config" xsi:type="array"> <item name="template" xsi:type="string">Magelearn_AdvanceCart/coupon</item> </item> </item> <item name="totals" xsi:type="array"> <item name="component" xsi:type="string">Magelearn_AdvanceCart/js/totals</item> <item name="config" xsi:type="array"> <item name="template" xsi:type="string">Magelearn_AdvanceCart/totals</item> </item> </item> <item name="sliding-cart" xsi:type="array"> <item name="component" xsi:type="string">Magelearn_AdvanceCart/js/sliding-cart</item> </item> </item> </argument> </arguments> </referenceBlock> </body> </page>
As per highlighted in above xml file, we will add our template file at app/code/Magelearn/AdvanceCart/view/frontend/templates/minicart_open.phtml
<script type="text/x-magento-init"> { "[data-block='minicart']" : { "Magelearn_MinicartOpen/js/view/minicartopen" : {} } }Now will call our JS component file.
Create app/code/Magelearn/AdvanceCart/view/frontend/web/js/view/minicartopen.js file.
define(["jquery/ui", "jquery"], function(Component, $) { return function(config, element) { var minicart = $(element); minicart.on('contentLoading', function() { minicart.on('contentUpdated', function() { minicart.find('[data-role="dropdownDialog"]').dropdownDialog("open"); }); }); } });
Now we will check how to add product Short description in minicart items.
For that first we will add app/code/Magelearn/AdvanceCart/etc/catalog_attributes.xml file.
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Catalog:etc/catalog_attributes.xsd"> <group name="quote_item"> <attribute name="short_description"/> </group> </config>
Now we will add our plugin file at app/code/Magelearn/AdvanceCart/etc/frontend/di.xml to add additional data into the cart items.
We can also do the same thing with preference but plugin is more preferable over preference.
<?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\Checkout\CustomerData\Cart"> <plugin name="magelearn_advancecart_grand_total" type="Magelearn\AdvanceCart\Plugin\Checkout\CustomerData\Cart"/> </type> <type name="Magento\Checkout\CustomerData\DefaultItem"> <plugin name="AddDesPlug" type="Magelearn\AdvanceCart\Plugin\DefaultItem" disabled="false" sortOrder="10"/> </type> </config>
Now create a plugin file at app/code/Magelearn/AdvanceCart/Plugin/DefaultItem.php and add additional data for product's items.
<?php namespace Magelearn\AdvanceCart\Plugin; use Magento\Quote\Model\Quote\Item; class DefaultItem { public function aroundGetItemData($subject, \Closure $proceed, Item $item) { $data = $proceed($item); $atts = []; $product_desc = $item->getProduct()->getShortDescription(); $atts = [ "short_description" => $product_desc ]; return array_merge($data, $atts); } }
Now we will override vendor/magento/module-checkout/view/frontend/web/template/minicart/item/default.html file with our custom module file.
For that we need to add entry in requirejs-config.js file.
Add app/code/Magelearn/AdvanceCart/view/frontend/requirejs-config.js file.
var config = { paths: { 'slick': "Magelearn_AdvanceCart/js/slick" }, map: { '*': { 'Magento_Checkout/template/minicart/item/default.html': 'Magelearn_AdvanceCart/template/minicart/item/default.html', 'sidebar': 'Magelearn_AdvanceCart/js/sidebar', 'Magento_Checkout/js/view/minicart': 'Magelearn_AdvanceCart/js/view/minicart' } }, shim: { 'slick': { deps: ['jquery'] } } };
Now add app/code/Magelearn/AdvanceCart/view/frontend/web/template/minicart/item/default.html file.
<!-- /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ --> <li class="item product product-item" data-role="product-item"> <div class="product"> <!-- ko if: product_has_url --> <a data-bind="attr: {href: product_url, title: product_name}" tabindex="-1" class="product-item-photo"> <!-- ko foreach: $parent.getRegion('itemImage') --> <!-- ko template: {name: getTemplate(), data: item.product_image} --><!-- /ko --> <!-- /ko --> </a> <!-- /ko --> <!-- ko ifnot: product_has_url --> <span class="product-item-photo"> <!-- ko foreach: $parent.getRegion('itemImage') --> <!-- ko template: {name: getTemplate(), data: item.product_image} --><!-- /ko --> <!-- /ko --> </span> <!-- /ko --> <div class="product-item-details"> <strong class="product-item-name"> <!-- ko if: product_has_url --> <a data-bind="attr: {href: product_url}, html: $parent.getProductNameUnsanitizedHtml(product_name)"></a> <!-- /ko --> <!-- ko ifnot: product_has_url --> <span data-bind="html: $parent.getProductNameUnsanitizedHtml(product_name)"></span> <!-- /ko --> <p class="productDesc"> <!-- ko if: short_description --> <span data-bind="html: $parent.getProductNameUnsanitizedHtml(short_description)"></span> <!-- /ko --> </p> </strong> <!-- ko if: options.length --> <div class="product options" data-mage-init='{"collapsible":{"openedState": "active", "saveState": false}}'> <span data-role="title" class="toggle"><!-- ko i18n: 'See Details' --><!-- /ko --></span> <div data-role="content" class="content"> <strong class="subtitle"><!-- ko i18n: 'Options Details' --><!-- /ko --></strong> <dl class="product options list"> <!-- ko foreach: { data: options, as: 'option' } --> <dt class="label"><!-- ko text: option.label --><!-- /ko --></dt> <dd class="values"> <!-- ko if: Array.isArray(option.value) --> <span data-bind="html: $parents[1].getOptionValueUnsanitizedHtml(option.value.join('<br/>'))"></span> <!-- /ko --> <!-- ko if: (!Array.isArray(option.value) && ['file', 'html'].includes(option.option_type)) --> <span data-bind="html: $parents[1].getOptionValueUnsanitizedHtml(option.value)"></span> <!-- /ko --> <!-- ko if: (!Array.isArray(option.value) && !['file', 'html'].includes(option.option_type)) --> <span data-bind="text: option.value"></span> <!-- /ko --> </dd> <!-- /ko --> </dl> </div> </div> <!-- /ko --> <div class="product-item-pricing"> <!-- ko if: canApplyMsrp --> <div class="details-map"> <span class="label" data-bind="i18n: 'Price'"></span> <span class="value" data-bind="i18n: 'See price before order confirmation.'"></span> </div> <!-- /ko --> <!-- ko ifnot: canApplyMsrp --> <!-- ko foreach: $parent.getRegion('priceSidebar') --> <!-- ko template: {name: getTemplate(), data: item.product_price, as: 'price'} --><!-- /ko --> <!-- /ko --> <!-- /ko --> <div class="details-qty qty"> <label class="label" data-bind="i18n: 'Qty', attr: { for: 'cart-item-'+item_id+'-qty'}"></label> <button type="button" data-bind="attr: {title: $t('Decrease the quantity'), 'data-cart-item': item_id, 'data-item-qty': qty}" class="decreasing-qty">-</button> <input data-bind="attr: { id: 'cart-item-'+item_id+'-qty', 'data-cart-item': item_id, 'data-item-qty': qty, 'data-cart-item-id': product_sku}, value: qty" type="number" size="4" class="item-qty cart-item-qty" maxlength="12"/> <button type="button" data-bind="attr: {title: $t('Increase the quantity'), 'data-cart-item': item_id, 'data-item-qty': qty}" class="increasing-qty">+</button> <button data-bind="attr: { id: 'update-cart-item-'+item_id, 'data-cart-item': item_id, title: $t('Update') }" class="update-cart-item" style="display: none"> <span data-bind="i18n: 'Update'"></span> </button> </div> </div> <div class="product actions"> <!-- ko if: is_visible_in_site_visibility --> <div class="primary"> <a data-bind="attr: {href: configure_url, title: $t('Edit item')}" class="action edit"> <span data-bind="i18n: 'Edit'"></span> </a> </div> <!-- /ko --> <div class="secondary"> <a href="#" data-bind="attr: {'data-cart-item': item_id, title: $t('Remove item')}" class="action delete"> <span data-bind="i18n: 'Remove'"></span> </a> </div> </div> </div> </div> <div class="message notice" if="$data.message"> <div data-bind="text: $data.message"></div> </div> </li>
Now as per defined in requirejs-config.js file we will add app/code/Magelearn/AdvanceCart/view/frontend/web/js/view/minicart.js file.
define([ 'uiComponent', 'Magento_Customer/js/customer-data', 'jquery', 'ko', 'underscore', 'sidebar', 'mage/translate', 'mage/dropdown' ], function (Component, customerData, $, ko, _) { 'use strict'; var sidebarInitialized = false, addToCartCalls = 0, miniCart; miniCart = $('[data-block=\'minicart\']'); /** * @return {Boolean} */ function initSidebar() { if (miniCart.data('mageSidebar')) { miniCart.sidebar('update'); } if (!$('[data-role=product-item]').length) { return false; } miniCart.trigger('contentUpdated'); if (sidebarInitialized) { return false; } sidebarInitialized = true; miniCart.sidebar({ 'targetElement': 'div.block.block-minicart', 'url': { 'checkout': window.checkout.checkoutUrl, 'update': window.checkout.updateItemQtyUrl, 'remove': window.checkout.removeItemUrl, 'loginUrl': window.checkout.customerLoginUrl, 'isRedirectRequired': window.checkout.isRedirectRequired }, 'button': { 'checkout': '#top-cart-btn-checkout', 'remove': '#mini-cart a.action.delete', 'close': '#btn-minicart-close' }, 'showcart': { 'parent': 'span.counter', 'qty': 'span.counter-number', 'label': 'span.counter-label' }, 'minicart': { 'list': '#mini-cart', 'content': '#minicart-content-wrapper', 'qty': 'div.items-total', 'subtotal': 'div.subtotal span.price', 'maxItemsVisible': window.checkout.minicartMaxItemsVisible }, 'item': { 'qty': ':input.cart-item-qty', 'button': ':button.update-cart-item', 'qtyDecreasing': '.decreasing-qty', 'qtyIncreasing': '.increasing-qty' }, 'confirmMessage': $.mage.__('Are you sure you would like to remove this item from the shopping cart?') }); } miniCart.on('dropdowndialogopen', function () { initSidebar(); }); return Component.extend({ shoppingCartUrl: window.checkout.shoppingCartUrl, maxItemsToDisplay: window.checkout.maxItemsToDisplay, cart: {}, /** * @override */ initialize: function () { var self = this, cartData = customerData.get('cart'); this.update(cartData()); cartData.subscribe(function (updatedCart) { addToCartCalls--; this.isLoading(addToCartCalls > 0); sidebarInitialized = false; this.update(updatedCart); initSidebar(); }, this); $('[data-block="minicart"]').on('contentLoading', function (event) { addToCartCalls++; self.isLoading(true); }); if (cartData().website_id !== window.checkout.websiteId) { customerData.reload(['cart'], false); } return this._super(); }, isLoading: ko.observable(false), initSidebar: initSidebar, /** * Close mini shopping cart. */ closeMinicart: function () { $('[data-block="minicart"]').find('[data-role="dropdownDialog"]').dropdownDialog('close'); }, /** * @param {String} productType * @return {*|String} */ getItemRenderer: function (productType) { return this.itemRenderer[productType] || 'defaultRenderer'; }, /** * Update mini shopping cart content. * * @param {Object} updatedCart * @returns void */ update: function (updatedCart) { _.each(updatedCart, function (value, key) { if (!this.cart.hasOwnProperty(key)) { this.cart[key] = ko.observable(); } this.cart[key](value); }, this); }, /** * Get cart param by name. * * @param {String} name * @returns {*} */ getCartParamUnsanitizedHtml: function (name) { if (!_.isUndefined(name)) { if (!this.cart.hasOwnProperty(name)) { this.cart[name] = ko.observable(); } } return this.cart[name](); }, /** * Get cart param by name. * * @param {String} name * @returns {*} */ getCartParam: function (name) { return this.getCartParamUnsanitizedHtml(name); }, /** * Returns array of cart items, limited by 'maxItemsToDisplay' setting. * * @returns [] */ getCartItems: function () { var items = this.getCartParam('items') || []; items = items.slice(parseInt(-this.maxItemsToDisplay, 10)); return items; }, /** * Returns count of cart line items. * * @returns {Number} */ getCartLineItemsCount: function () { var items = this.getCartParam('items') || []; return parseInt(items.length, 10); } }); });
Now as per defined in requirejs-config.js file we will add our JS component file at
app/code/Magelearn/AdvanceCart/view/frontend/web/js/sidebar.js to add +/- button functionality in minicart items.
/** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ define([ 'jquery', 'Magento_Customer/js/model/authentication-popup', 'Magento_Customer/js/customer-data', 'Magento_Ui/js/modal/alert', 'Magento_Ui/js/modal/confirm', 'underscore', 'jquery-ui-modules/widget', 'mage/decorate', 'mage/collapsible', 'mage/cookies', 'jquery-ui-modules/effect-fade' ], function ($, authenticationPopup, customerData, alert, confirm, _) { 'use strict'; $.widget('mage.sidebar', { options: { isRecursive: true, minicart: { maxItemsVisible: 3 } }, scrollHeight: 0, shoppingCartUrl: window.checkout.shoppingCartUrl, /** * Create sidebar. * @private */ _create: function () { this._initContent(); }, /** * Update sidebar block. */ update: function () { $(this.options.targetElement).trigger('contentUpdated'); this._calcHeight(); this._isOverflowed(); }, /** * @private */ _initContent: function () { var self = this, events = {}; this.element.decorate('list', this.options.isRecursive); /** * @param {jQuery.Event} event */ events['click ' + this.options.button.close] = function (event) { event.stopPropagation(); $(self.options.targetElement).dropdownDialog('close'); }; events['click ' + this.options.button.checkout] = $.proxy(function () { var cart = customerData.get('cart'), customer = customerData.get('customer'), element = $(this.options.button.checkout); if (!customer().firstname && cart().isGuestCheckoutAllowed === false) { // set URL for redirect on successful login/registration. It's postprocessed on backend. $.cookie('login_redirect', this.options.url.checkout); if (this.options.url.isRedirectRequired) { element.prop('disabled', true); location.href = this.options.url.loginUrl; } else { authenticationPopup.showModal(); } return false; } element.prop('disabled', true); location.href = this.options.url.checkout; }, this); /** * @param {jQuery.Event} event */ events['click ' + this.options.button.remove] = function (event) { event.stopPropagation(); confirm({ content: self.options.confirmMessage, actions: { /** @inheritdoc */ confirm: function () { self._removeItem($(event.currentTarget)); }, /** @inheritdoc */ always: function (e) { e.stopImmediatePropagation(); } } }); }; /** * @param {jQuery.Event} event */ events['keyup ' + this.options.item.qty] = function (event) { self._showItemButton($(event.target)); }; /** * @param {jQuery.Event} event */ events['change ' + this.options.item.qty] = function (event) { self._showItemButton($(event.target)); }; /** * @param {jQuery.Event} event */ events['click ' + this.options.item.button] = function (event) { event.stopPropagation(); self._updateItemQty($(event.currentTarget)); }; /** * @param {jQuery.Event} event */ events['focusout ' + this.options.item.qty] = function (event) { self._validateQty($(event.currentTarget)); }; // The bellow codes will execute when you click on the decrease button events['click ' + this.options.item.qtyDecreasing] = function (event) { event.stopPropagation(); var itemId = $(event.currentTarget).data('cart-item'); var qtyElement = $('#cart-item-' + itemId + '-qty'); var qtyValue = parseInt(qtyElement.val()); qtyValue = qtyValue - 1; if (qtyValue <= 0) { qtyValue = 1; } qtyElement.val(qtyValue).trigger('keyup'); }; // The bellow codes will execute when you click on the increase button events['click ' + this.options.item.qtyIncreasing] = function (event) { event.stopPropagation(); var itemId = $(event.currentTarget).data('cart-item'); var qtyElement = $('#cart-item-' + itemId + '-qty'); var qtyValue = parseInt(qtyElement.val()); qtyValue = qtyValue + 1; if (qtyValue > 100) { qtyValue = 100; } qtyElement.val(qtyValue).trigger('keyup'); }; this._on(this.element, events); this._calcHeight(); this._isOverflowed(); }, /** * Add 'overflowed' class to minicart items wrapper element * * @private */ _isOverflowed: function () { var list = $(this.options.minicart.list), cssOverflowClass = 'overflowed'; if (this.scrollHeight > list.innerHeight()) { list.parent().addClass(cssOverflowClass); } else { list.parent().removeClass(cssOverflowClass); } }, /** * @param {HTMLElement} elem * @private */ _showItemButton: function (elem) { var itemId = elem.data('cart-item'), itemQty = elem.data('item-qty'); if (this._isValidQty(itemQty, elem.val())) { $('#update-cart-item-' + itemId).show('fade', 300); } else if (elem.val() == 0) { //eslint-disable-line eqeqeq this._hideItemButton(elem); } else { this._hideItemButton(elem); } }, /** * @param {*} origin - origin qty. 'data-item-qty' attribute. * @param {*} changed - new qty. * @returns {Boolean} * @private */ _isValidQty: function (origin, changed) { return origin != changed && //eslint-disable-line eqeqeq changed.length > 0 && changed - 0 == changed && //eslint-disable-line eqeqeq changed - 0 > 0; }, /** * @param {Object} elem * @private */ _validateQty: function (elem) { var itemQty = elem.data('item-qty'); if (!this._isValidQty(itemQty, elem.val())) { elem.val(itemQty); } }, /** * @param {HTMLElement} elem * @private */ _hideItemButton: function (elem) { var itemId = elem.data('cart-item'); $('#update-cart-item-' + itemId).hide('fade', 300); }, /** * @param {HTMLElement} elem * @private */ _updateItemQty: function (elem) { var itemId = elem.data('cart-item'); this._ajax(this.options.url.update, { 'item_id': itemId, 'item_qty': $('#cart-item-' + itemId + '-qty').val() }, elem, this._updateItemQtyAfter); }, /** * Update content after update qty * * @param {HTMLElement} elem */ _updateItemQtyAfter: function (elem) { var productData = this._getProductById(Number(elem.data('cart-item'))); if (!_.isUndefined(productData)) { $(document).trigger('ajax:updateCartItemQty'); if (window.location.href === this.shoppingCartUrl) { window.location.reload(false); } } this._hideItemButton(elem); }, /** * @param {HTMLElement} elem * @private */ _removeItem: function (elem) { var itemId = elem.data('cart-item'); this._ajax(this.options.url.remove, { 'item_id': itemId }, elem, this._removeItemAfter); }, /** * Update content after item remove * * @param {Object} elem * @private */ _removeItemAfter: function (elem) { var productData = this._getProductById(Number(elem.data('cart-item'))); if (!_.isUndefined(productData)) { $(document).trigger('ajax:removeFromCart', { productIds: [productData['product_id']], productInfo: [ { 'id': productData['product_id'] } ] }); if (window.location.href.indexOf(this.shoppingCartUrl) === 0) { window.location.reload(); } } }, /** * Retrieves product data by Id. * * @param {Number} productId - product Id * @returns {Object|undefined} * @private */ _getProductById: function (productId) { return _.find(customerData.get('cart')().items, function (item) { return productId === Number(item['item_id']); }); }, /** * @param {String} url - ajax url * @param {Object} data - post data for ajax call * @param {Object} elem - element that initiated the event * @param {Function} callback - callback method to execute after AJAX success */ _ajax: function (url, data, elem, callback) { $.extend(data, { 'form_key': $.mage.cookies.get('form_key') }); $.ajax({ url: url, data: data, type: 'post', dataType: 'json', context: this, /** @inheritdoc */ beforeSend: function () { elem.attr('disabled', 'disabled'); }, /** @inheritdoc */ complete: function () { elem.attr('disabled', null); } }) .done(function (response) { var msg; if (response.success) { callback.call(this, elem, response); } else { msg = response['error_message']; if (msg) { alert({ content: msg }); } } }) .fail(function (error) { console.log(JSON.stringify(error)); }); }, /** * Calculate height of minicart list * * @private */ _calcHeight: function () { var self = this, height = 0, counter = this.options.minicart.maxItemsVisible, target = $(this.options.minicart.list), outerHeight; self.scrollHeight = 0; target.children().each(function () { if ($(this).find('.options').length > 0) { $(this).collapsible(); } outerHeight = $(this).outerHeight(true); if (counter-- > 0) { height += outerHeight; } self.scrollHeight += outerHeight; }); target.parent().height(height); } }); return $.mage.sidebar; });
Now as per defined in etc/frontend/di.xml file we will add our plugin file to add additional data into the cart object.
Create file app/code/Magelearn/AdvanceCart/Plugin/Checkout/CustomerData/Cart.php file.
<?php namespace Magelearn\AdvanceCart\Plugin\Checkout\CustomerData; use Magelearn\AdvanceCart\Helper\Data as helperData; class Cart { /** * @var Magelearn\AdvanceCart\Helper\Data */ protected $helperData; protected $layoutFactory; protected $checkoutSession; protected $checkoutHelper; protected $quote; /** * @var \Magento\Quote\Api\CartTotalRepositoryInterface */ protected $cartTotalRepository; /** * @var \Magento\Framework\Pricing\Helper\Data */ protected $priceHelper; /** * @var \Magento\Quote\Model\QuoteIdMaskFactory */ protected $quoteIdMaskFactory; /** * @var \Magento\Customer\Model\Session */ protected $customerSession; /** * @var \Magento\Framework\UrlInterface */ protected $urlInterface; protected $_logger; public function __construct( \Magento\Framework\View\LayoutFactory $layoutFactory, helperData $helperData, \Magento\Checkout\Model\Session $checkoutSession, \Magento\Checkout\Helper\Data $checkoutHelper, \Magento\Quote\Api\CartTotalRepositoryInterface $cartTotalRepository, \Magento\Framework\Pricing\Helper\Data $priceHelper, \Magento\Quote\Model\QuoteIdMaskFactory $quoteIdMaskFactory, \Magento\Customer\Model\Session $customerSession, \Magento\Framework\UrlInterface $urlInterface, \Psr\Log\LoggerInterface $logger ) { $this->layoutFactory = $layoutFactory; $this->helperData = $helperData; $this->checkoutSession = $checkoutSession; $this->checkoutHelper = $checkoutHelper; $this->cartTotalRepository = $cartTotalRepository; $this->priceHelper = $priceHelper; $this->quoteIdMaskFactory = $quoteIdMaskFactory; $this->customerSession = $customerSession; $this->urlInterface = $urlInterface; $this->_logger = $logger; } /** * Add grand total to result * * @param \Magento\Checkout\CustomerData\Cart $subject * @param array $result * @return array */ public function afterGetSectionData( \Magento\Checkout\CustomerData\Cart $subject, $result ) { $upsell_view = $this->helperData->getGeneralConfig('upsell_view'); if($upsell_view == 1) { $upselldata = $this->layoutFactory->create() ->createBlock(\Magelearn\AdvanceCart\Block\Cart\Upsell::class) ->setTemplate('Magelearn_AdvanceCart::minicart/slider.phtml') ->toHtml(); $result['advancecart']['upsell_data'] = $upselldata; } else { $upselldata = $this->layoutFactory->create() ->createBlock(\Magelearn\AdvanceCart\Block\Cart\Upsell::class) ->setTemplate('Magelearn_AdvanceCart::minicart/grid.phtml') ->toHtml(); $result['advancecart']['upsell_data'] = $upselldata; } $result['advancecart']['coupon_enable'] = boolval($this->helperData->getGeneralConfig('coupon')); $result['advancecart']['totals_enable'] = boolval($this->helperData->getGeneralConfig('totals')); $result['advancecart']['totals_title'] = $this->helperData->getGeneralConfig('totals_title'); $coupon_code = $this->getQuote()->getCouponCode(); $result['advancecart']['coupon_code'] = ($coupon_code != '' ? $coupon_code : null); $quoteId = $this->getQuote()->getId(); $quoteIdMask = $this->quoteIdMaskFactory->create()->load($quoteId, 'quote_id'); if(isset($quoteId) && $quoteId != null) { $totals = $this->cartTotalRepository->get($quoteId)->getTotalSegments(); } else { $totals = ''; } $_totals = []; if(isset($totals) && $totals != '') { foreach ($totals as $key => $value) { $_totals[] = [ 'title' => $value->getTitle(), 'value' => $this->priceHelper->currency($value->getValue(), true, false), ]; } } $result['advancecart']['totals'] = $_totals; $result['advancecart']['grand_total'] = isset($totals['grand_total']) ? $this->priceHelper->currency($totals['grand_total']->getValue(), true, false) : 0; $isLoggedIn = $this->customerSession->isLoggedIn(); $result['advancecart']['isLoggedIn'] = ($isLoggedIn != '' ? true : false); $result['advancecart']['apiUrl'] = $this->urlInterface->getBaseUrl().'rest/default/V1/'; $result['advancecart']['quoteId'] = $quoteIdMask->getMaskedId(); return $result; } /** * Get active quote. * * @return Quote */ protected function getQuote() { if ($this->quote === null) { $this->quote = $this->checkoutSession->getQuote(); } return $this->quote; } }
Now add app/code/Magelearn/AdvanceCart/Helper/Data.php file.
<?php namespace Magelearn\AdvanceCart\Helper; use Magento\Framework\App\Helper\AbstractHelper; use Magento\Store\Model\ScopeInterface; class Data extends AbstractHelper { const XML_PATH_ADVANCECART = 'advancecart_setting/'; /** * Get ConfigValue. * * @return {Boolean} */ public function getConfigValue($field, $storeId = null) { return $this->scopeConfig->getValue( $field, ScopeInterface::SCOPE_STORE, $storeId ); } /** * Get GeneralConfig. * * @return {String} */ public function getGeneralConfig($code, $storeId = null) { return $this->getConfigValue(self::XML_PATH_ADVANCECART.'minicart_settings/'.$code, $storeId); } }
Now we will create our block and template files to get Up-sell items data and display it.
Create app/code/Magelearn/AdvanceCart/Block/Cart/Upsell.php file.
<?php namespace Magelearn\AdvanceCart\Block\Cart; /** * Upsell content block */ class Upsell extends \Magento\Framework\View\Element\Template { protected $_productCollectionFactory; protected $_imageHelper; protected $_cartHelper; protected $_imageBuilder; protected $_scopeConfig; protected $_productRepository; protected $_checkoutSession; protected $_cart; protected $_storeScope; protected $_priceHelper; protected $_formKey; protected $_logger; const XML_PATH_KEY = 'advancecart_setting/minicart_settings/'; public function __construct( \Magento\Framework\View\Element\Template\Context $context, \Magento\Catalog\Block\Product\Context $productcontext, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Checkout\Model\Cart $cart, \Magento\Checkout\Model\Session $checkoutSession, \Magento\Catalog\Api\ProductRepositoryInterface $productRepository, \Magento\Framework\Pricing\Helper\Data $priceHelper, \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory, \Magento\Framework\Data\Form\FormKey $formKey, \Psr\Log\LoggerInterface $logger ) { $this->_productCollectionFactory = $productCollectionFactory; $this->_imageHelper = $productcontext->getImageHelper(); $this->_imageBuilder = $productcontext->getImageBuilder(); $this->_cartHelper = $productcontext->getCartHelper(); $this->_scopeConfig = $scopeConfig; $this->_cart = $cart; $this->_checkoutSession = $checkoutSession; $this->_productRepository = $productRepository; $this->_storeScope = \Magento\Store\Model\ScopeInterface::SCOPE_STORE; $this->_priceHelper = $priceHelper; $this->_formKey = $formKey; $this->_logger = $logger; parent::__construct($context); } public function getTitle() { $upsell_title = $this->_scopeConfig->getValue(self::XML_PATH_KEY.'upsell_title', $this->_storeScope); return $upsell_title; } public function getProductCollection() { $upsell_limit = $this->_scopeConfig->getValue(self::XML_PATH_KEY.'upsell_limit', $this->_storeScope); $upsell_sku = $this->_scopeConfig->getValue(self::XML_PATH_KEY.'upsell_sku', $this->_storeScope); $upsell_type = $this->_scopeConfig->getValue(self::XML_PATH_KEY.'upsell_type', $this->_storeScope); $ids = []; $productInfo = $this->_cart->getQuote()->getItemsCollection(); foreach ($productInfo as $item){ $ids[] = $item['product_id']; } $sku = []; if ($upsell_sku != '') { if (strpos($upsell_sku, ',') !== false) { $sku1 = explode(',', $upsell_sku); foreach ($sku1 as $skuchild) { if (trim($skuchild) != '') { $sku[] = trim($skuchild); } } } else { $sku = [trim($upsell_sku)]; } } $collection = $this->_productCollectionFactory->create(); $collection->addAttributeToSelect('*'); $collection->addFilter('type_id', 'simple'); try { if ($upsell_type == '1') { $ids = $this->getUpsellProductID(); if (count($ids) > 0) { $collection->addFieldToFilter('entity_id', ['in' => $ids]); } } else { if (count($sku) > 0) { $collection->addFieldToFilter('sku', ['in' => $sku]); } } } catch (\Exception $e) { return false; } $collection->setPageSize($upsell_limit); // fetching only limited products return $collection; } public function getUpsellProductID() { $upsellProduct = []; try { $productInfo = $this->_cart->getQuote()->getItemsCollection(); if (count($productInfo) == 0) { return $upsellProduct; } foreach ($productInfo as $item) { $product = $this->_productRepository->getById($item->getProductId()); $upsell = $product->getUpSellProducts(); if (count($upsell)) { foreach ($upsell as $item) { $upsellProduct[$item->getProductId()][] = $item->getId(); } } } if (count($upsellProduct) == 0) { return $upsellProduct; } $finalarray = []; $cnt = 0; for ($i=0; $i<1000; $i++) { foreach ($upsellProduct as $upsellProduct1) { foreach ($upsellProduct1 as $upsellProduct2) { if (!in_array($upsellProduct2, $finalarray)) { $finalarray[]= $array2; break; } } } $i++; if (count($finalarray) >= $cnt) { break; } } return $finalarray; } catch (\Exception $e) { return $upsellProduct; } return $upsellProduct; } public function hasProductUrl($product) { if(strtolower($product->getAttributeText('visibility')) == 'not visible individually') { return false; } else { return true; } return false; } public function getProductUrl($product, $additional = []) { if ($this->hasProductUrl($product)) { if (!isset($additional['_escape'])) { $additional['_escape'] = true; } return $product->getUrlModel()->getUrl($product, $additional); } return '#'; } public function getImage($product, $imageId, $attributes = []) { return $this->_imageBuilder->create($product, $imageId, $attributes); } public function getAddToCartUrl($product, $additional = []) { if (!$product->getTypeInstance()->isPossibleBuyFromList($product)) { if (!isset($additional['_escape'])) { $additional['_escape'] = true; } if (!isset($additional['_query'])) { $additional['_query'] = []; } $additional['_query']['options'] = 'cart'; return $this->getProductUrl($product, $additional); } return $this->_cartHelper->getAddUrl($product, $additional); } public function getFormattedCurrency($price) { return $this->_priceHelper->currency($price, true, false); } public function getFormKey() { return $this->_formKey->getFormKey(); } }
We can display this upsell products data either in scroll view or with Slider (slick slider) view.
To display this data in simple scroll view create file at app/code/Magelearn/AdvanceCart/view/frontend/templates/minicart/grid.phtml
<?php $image = 'new_products_content_widget_grid'; $collection = $block->getProductCollection(); $items = $collection->getItems(); $showWishlist = true; $showCompare = true; $showCart = true; $templateType = \Magento\Catalog\Block\Product\ReviewRendererInterface::DEFAULT_VIEW; $description = false; ?><?php if ($exist = ($collection && $collection->getSize())): ?> <div class="dontForgetWrapper"> <p class="dontForget text-center"> <?php if ($block->getTitle()): ?> <?= $block->escapeHtml(__($block->getTitle())) ?> <?php endif ?> </p> <div class="dontForgetCard d-flex"> <div class="block widget block-products-list"> <div class="block-content"> <div class="products-list custom-top-slider"> <ol class="product-items"> <?php $iterator = 1; ?> <?php foreach ($items as $_item): ?> <?= /* @noEscape */ ($iterator++ == 1) ? '<li class="product-item">' : '</li><li class="product-item">' ?> <div class="product-item-info"> <a href="<?= $block->escapeUrl($block->getProductUrl($_item)) ?>" class="product-item-photo dontForgetImg"> <?= $block->getImage($_item, $image)->toHtml() ?> </a> <div class="product-item-details"> <div class="dontForgetInfoWrapper"> <a title="<?= $block->escapeHtml($_item->getName()) ?>" href="<?= $block->escapeUrl($block->getProductUrl($_item)) ?>" class="product-item-link"> <?php $product_title = $_item->getName(); $product_type = $_item->getTypeId(); $product_description = $_item->getShortDescription(); if ($product_title) { ?> <h3 class="productName"><?= /* @noEscape */ $product_title; ?></h3> <?php } if ($product_type) { ?> <h6 class="productCategory"><?= /* @noEscape */ $product_type; ?></h6> <?php }if ($product_description) { ?> <p class="productMisc"><?= /* @noEscape */ $product_description; ?></p> <?php } ?> </a> </div> <?php if ($showWishlist || $showCompare || $showCart): ?> <div class="product-item-actions productAddWrapper"> <?php if ($showCart): ?> <div class="actions-primary addProduct d-flex"> <?php if ($_item->isSaleable()): ?> <form data-role="minicart-form" class="minicartForm" action="<?= /* @noEscape */ $block->getAddToCartUrl($_item);?>" method="post"> <input type="hidden" name="form_key" value="<?= /* @noEscape */ $block->getFormKey() ?>" /> <p class="addProduct d-flex">ADD <button class="addBtn action tocart primary upsellbutton" type="submit"> <span><?= $block->escapeHtml(__('+')) ?></span> </button> </p> </form> <?php endif; ?> </div> <?php endif; $formattedPrice = $block->getFormattedCurrency($_item->getPrice()); ?> <div class="upsell_price"><?= /* @noEscape */ $formattedPrice; ?></div> </div> <?php endif; ?> </div> </div> <?= ($iterator == count($items) + 1) ? '</li>' : '' ?> <?php endforeach ?> </ol> </div> </div> </div> </div> </diV> <?php endif;?>
To display data in slider view create file at app/code/Magelearn/AdvanceCart/view/frontend/templates/minicart/slider.phtml
<?php $image = 'new_products_content_widget_grid'; $collection = $block->getProductCollection(); $items = $collection->getItems(); $showWishlist = true; $showCompare = true; $showCart = true; $templateType = \Magento\Catalog\Block\Product\ReviewRendererInterface::DEFAULT_VIEW; $description = false; ?><?php if ($exist = ($collection && $collection->getSize())): ?> <div class="dontForgetWrapper"> <p class="dontForget text-center"> <?php if ($block->getTitle()): ?> <?= $block->escapeHtml(__($block->getTitle())) ?> <?php endif ?> </p> <div class="dontForgetCard d-flex"> <div class="block widget block-products-list"> <div class="block-content"> <div class="products-list custom-top-slider"> <?php foreach ($items as $_item): ?> <div class="item"> <a href="<?= $block->escapeUrl($block->getProductUrl($_item)) ?>" class="product-item-photo dontForgetImg"> <?= $block->getImage($_item, $image)->toHtml(); ?> </a> <div class="item-details"> <div class="dontForgetInfoWrapper"> <a title="<?= $block->escapeHtml($_item->getName()) ?>" href="<?= $block->escapeUrl($block->getProductUrl($_item)) ?>" class="product-item-link"> <?php $product_title = $_item->getName(); $product_type = $_item->getTypeId(); $product_description = $_item->getShortDescription(); ?> <?php if ($product_title): ?> <p class="productName"><?= /* @noEscape */ $product_title; ?></p> <?php endif; ?> </a> <?php if ($product_type): ?> <p class="productCategory"><?= /* @noEscape */ $product_type; ?></p> <?php endif; ?> <?php if ($product_description): ?> <p class="productMisc"><?= /* @noEscape */ $product_description; ?></p> <?php endif; ?> </div> <?php if ($showWishlist || $showCompare || $showCart): ?> <div class="product-item-actions productAddWrapper"> <?php if ($showCart): ?> <div class="actions-primary addProduct d-flex"> <?php if ($_item->isSaleable()): ?> <form data-role="minicart-form" class="minicartForm" action="<?= /* @noEscape */ $block->getAddToCartUrl($_item);?>" method="post"> <input type="hidden" name="form_key" value="<?= /* @noEscape */ $block->getFormKey() ?>" /> <p class="addProduct d-flex">ADD <button class="addBtn action tocart primary upsellbutton" type="submit"> <span><?= $block->escapeHtml(__('+')) ?></span> </button> </p> </form> <?php endif; ?> </div> <?php endif; $formattedPrice = $block->getFormattedCurrency($_item->getPrice()); ?> <div class="upsell_price"><?= /* @noEscape */ $formattedPrice; ?></div> </div> <?php endif; ?> </div> </div> <?php endforeach ?> </div> </div> </div> </div> </div> <?php endif;?> <script> require(["jquery","slick"],function($,slick){ $(document).ready(function(){ $('.custom-top-slider').slick({ slidesToShow: 3, slidesToScroll: 1, dots: true, arrows: true, centerMode: true, centerPadding: 0, focusOnSelect: true, responsive: [ { breakpoint: 767, settings: { slidesToShow: 3, } }, { breakpoint: 560, settings: { slidesToShow: 3, } }, { breakpoint: 480, settings: { slidesToShow: 2, } } ] }); }) }) </script>
Now to display this upsell products items data into slick slider, we already defined its dependancy in requirejs-config.js file.
As per it create file at app/code/Magelearn/AdvanceCart/view/frontend/web/js/slick.js
Now as per defined in view/frontend/layout/default.xml file, to add this upsell products items into the cart, we will add minicart-data.js file.
Add app/code/Magelearn/AdvanceCart/view/frontend/web/js/minicart-data.js
// Ready function require(['jquery'], function ($) { $(document).ready(function (e) { $(document).on('click', '.minicart-wrapper .addProduct:not(.disabled)', function(e) { e.preventDefault(); var formele = $(this).closest("form"); var keyval = formele.find('input').val(); if(!keyval){ return false; } $(this).addClass('disabled'); var formData = new FormData(); formData.append( 'form_key', formele.find('input').val()); $.ajax({ url: formele.attr('action'), data: formData, type: 'post', dataType: 'json', cache: false, contentType: false, processData: false, success: function (res) { setTimeout(function(){ if(!$('.minicart-wrapper').hasClass('active')){ $('[data-block="minicart"]').find('[data-role="dropdownDialog"]').dropdownDialog("open"); } },3000); $(this).removeClass('disabled'); }, /** @inheritdoc */ error: function (res) { $(this).removeClass('disabled'); }, /** @inheritdoc */ complete: function (res) { $(this).removeClass('disabled'); } }); return false; }); }) })Now as per defined in view/frontend/layout/default.xml file, we will add our CSS files to display this data properly.
Now we will start to modify content in Minicart.
For that as per defined in view/frontend/layout/default.xml file, we will add app/code/Magelearn/AdvanceCart/view/frontend/web/template/minicart/content.html file.
<!-- /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ --> <div class="block-title"> <strong> <span class="text" translate="'My Cart'"></span> <span class="qty empty" text="getCartParam('summary_count')" data-bind="css: { empty: !!getCartParam('summary_count') == false }, attr: { title: $t('Items in Cart') }"> </span> </strong> </div> <div class="block-content"> <div class="extra"> <span class="price-wrapper freeShipping text-center active" data-bind="html: getCartParamUnsanitizedHtml('advancecart.extra_data')"></span> </div> <button type="button" id="btn-minicart-close" class="action close" data-action="close" data-bind=" attr: { title: $t('Close') }, click: closeMinicart() "> <span translate="'Close'"></span> </button> <if args="getCartParam('summary_count')"> <div class="items-total"> <span class="count" if="maxItemsToDisplay < getCartLineItemsCount()" text="maxItemsToDisplay"></span> <translate args="'of'" if="maxItemsToDisplay < getCartLineItemsCount()"></translate> <span class="count" text="getCartParam('summary_count')"></span> <!-- ko if: (getCartParam('summary_count') > 1) --> <span translate="'Items in Cart'"></span> <!--/ko--> <!-- ko if: (getCartParam('summary_count') === 1) --> <span translate="'Item in Cart'"></span> <!--/ko--> </div> <each args="getRegion('subtotalContainer')" render=""></each> <each args="getRegion('extraInfo')" render=""></each> <div class="actions" if="getCartParam('possible_onepage_checkout')"> <div class="primary"> <button id="top-cart-btn-checkout" type="button" class="action primary checkout" data-action="close" data-bind=" attr: { title: $t('Proceed to Checkout') }, click: closeMinicart() " translate="'Proceed to Checkout'"> </button> <div data-bind="html: getCartParamUnsanitizedHtml('extra_actions')"></div> </div> </div> </if> <if args="getCartParam('summary_count')"> <strong class="subtitle" translate="'Recently added item(s)'"></strong> <div data-action="scroll" class="minicart-items-wrapper"> <ol id="mini-cart" class="minicart-items" data-bind="foreach: { data: getCartItems(), as: 'item' }"> <each args="$parent.getRegion($parent.getItemRenderer(item.product_type))" render="{name: getTemplate(), data: item, afterRender: function() {$parents[1].initSidebar()}}"></each> </ol> </div> </if> <ifnot args="getCartParam('summary_count')"> <strong class="subtitle empty" translate="'You have no items in your shopping cart.'"></strong> <if args="getCartParam('cart_empty_message')"> <p class="minicart empty text" text="getCartParam('cart_empty_message')"></p> <div class="actions"> <div class="secondary"> <a class="action viewcart" data-bind="attr: {href: shoppingCartUrl}"> <span translate="'View and Edit Cart'"></span> </a> </div> </div> </if> </ifnot> <div class="actions" if="getCartParam('summary_count')"> <div class="secondary"> <a class="action viewcart" data-bind="attr: {href: shoppingCartUrl}"> <span translate="'View and Edit Cart'"></span> </a> </div> </div> <div id="minicart-widgets" class="minicart-widgets" if="regionHasElements('promotion')"> <each args="getRegion('promotion')" render=""></each> </div> <div data-bind="scope:'sliding-cart'"> <if args="isTotalsEnabled()"> <div id="totals" data-bind="scope:'totals'"> <div class="totals-title" data-bind="text:getTotalsTitle()"></div> <div id="coupon" data-bind="scope:'coupon'"> <!-- ko template: getTemplate() --><!-- /ko --> </div> <!-- ko template: getTemplate() --><!-- /ko --> </div> </if> </div> </div> <each args="getRegion('sign-in-popup')" render=""></each>
As per defined in view/frontend/layout/default.xml file to display upsell products, we will add app/code/Magelearn/AdvanceCart/view/frontend/web/template/minicart/total/grand-total.html file.
<!-- ko if: cart().summary_count && cart().summary_count > 0 --> <div class="upsell-data" data-bind="html: cart().advancecart.upsell_data"> </div> <!-- /ko -->
Now we will add JS component and html files for coupon and totals as per defined in view/frontend/layout/default.xml file.
Add app/code/Magelearn/AdvanceCart/view/frontend/web/template/coupon.html file.
<div data-bind="visible: isVisible()"> <form class="cart-discount" id="slidingcart-coupon-form" method="post"> <div role="alert" class="message message-error error" data-bind="visible: errorMsg"> <div data-bind="text: errorMsg"></div> </div> <div role="alert" class="message message-success success" data-bind="visible: successMsg"> <div data-bind="text: successMsg"></div> </div> <div class="fieldset coupon"> <div class="field"> <label for="slidingcart-coupon-code" class="label"></label> <div class="control"> <input class="input-text" type="text" id="slidingcart-coupon-code" name="coupon_code" data-bind="value: couponCode, attr: {disabled:isApplied(), placeholder: $t('Enter discount code')} "/> </div> </div> <div class="actions-toolbar"> <div class="primary"> <!-- ko ifnot: isApplied() --> <button class="action action-apply" type="button" data-bind="value: $t('Apply'), click: apply"> <span><!-- ko i18n: 'Apply'--><!-- /ko --></span> </button> <!-- /ko --> <!-- ko if: isApplied() --> <input type="hidden" name="remove" id="remove-coupon" value="1"/> <button class="action action-cancel" type="button" data-bind="value: $t('Cancel'), click: cancel"> <span><!-- ko i18n: 'Cancel'--><!-- /ko --></span> </button> <!-- /ko --> </div> </div> </div> </form> </div>
Add JS component file at app/code/Magelearn/AdvanceCart/view/frontend/web/js/coupon.js
define([ 'ko', 'jquery', 'uiComponent', 'Magento_Customer/js/customer-data', 'mage/translate' ], function (ko, $, Component, customerData, $t) { 'use strict'; var guestUrl = 'guest-carts/:cartId/coupons/:couponCode', customerUrl = 'carts/mine/coupons/:couponCode'; var cartData = customerData.get('cart')(); return Component.extend({ /** * @return {Boolean} */ isVisible: function(){ if(Object.keys(customerData.get('cart')()).length > 0){ var cartData = customerData.get('cart')(); return ko.observable(Boolean(cartData.advancecart.coupon_enable)); } return ko.observable(false); }, couponCode: ko.observable(), isApplied: ko.observable(), quoteId: ko.observable(), isLoggedIn: ko.observable(), apiUrl: ko.observable(), errorMsg: ko.observable(), successMsg: ko.observable(), hideMsgTimeout: null, initialize: function () { this._super(); this.initAdvanceCartData(customerData.get('cart')()); return this; }, initObservable: function () { var self = this; this._super(); customerData.get('cart').subscribe(function (cartData) { self.initAdvanceCartData(cartData); }); return this; }, initAdvanceCartData: function (cartData) { if (cartData.hasOwnProperty('advancecart')) { this.couponCode(cartData.advancecart.coupon_code); this.isApplied(!!cartData.advancecart.coupon_code); this.isLoggedIn(cartData.advancecart.isLoggedIn); this.quoteId(cartData.advancecart.quoteId); this.apiUrl(cartData.advancecart.apiUrl); } }, handleMsg: function (type) { $('#slidingcart-coupon-form .message-' + type).show(); this.hideMsgTimeout = setTimeout(function () { $('#slidingcart-coupon-form .message-' + type).hide('blind', {}, 500); }, 3000); }, apply: function () { var self = this, field = $('#slidingcart-coupon-code'); clearTimeout(this.hideMsgTimeout); if (!this.couponCode()) { field.focus().trigger('focusin'); field.css('border-color', '#ed8380'); return; } field.css('border-color', ''); $.ajax({ method: 'put', contentType: 'application/json', showLoader: true, url: this.buildUrl(this.quoteId(), this.couponCode()), data: JSON.stringify({ quoteId: this.quoteId(), couponCode: this.couponCode() }), success: function () { var cartData = customerData.get('cart')(); cartData.couponCode = self.couponCode(); customerData.set('cart', cartData); self.handleSuccessApply(); self.handleMsg('success'); }, error: function (response) { self.handleErrorResponse(response); self.handleMsg('error'); } }); }, cancel: function () { var self = this; clearTimeout(this.hideMsgTimeout); $.ajax({ method: 'delete', contentType: 'application/json', showLoader: true, url: this.buildUrl(this.quoteId(), ''), data: JSON.stringify({quoteId: this.quoteId()}), success: function () { var cartData = customerData.get('cart')(); cartData.couponCode = ''; customerData.set('cart', cartData); self.handleSuccessCancel(); self.handleMsg('success'); }, error: function (response) { self.handleErrorResponse(response); self.handleMsg('error'); } }); }, handleSuccessApply: function () { customerData.reload(['cart'], false); this.successMsg($t('Your coupon was successfully applied.')); }, handleSuccessCancel: function () { customerData.reload(['cart'], false); this.successMsg($t('Your coupon was successfully removed.')); }, handleErrorResponse: function (response) { this.errorMsg(response.responseJSON ? response.responseJSON.message : ''); }, buildUrl: function (cartId, couponCode) { var url = guestUrl; if (this.isLoggedIn()) { url = customerUrl; } return this.apiUrl() + url.replace(':cartId', cartId).replace(':couponCode', couponCode); } }); });
Now add total's html file at app/code/Magelearn/AdvanceCart/view/frontend/web/template/totals.html
<div data-bind="visible: isVisible()"> <table> <tbody> <!-- ko foreach: getTotals() --> <tr class="totals"> <th data-bind="i18n: title" class="mark" scope="row"></th> <td class="amount"> <span class="price" data-bind="text:value"></span> </td> </tr> <!-- /ko --> </tbody> </table> </div>
Now we will add total's JS component file at app/code/Magelearn/AdvanceCart/view/frontend/web/js/totals.js
define([ 'ko', 'uiComponent', 'Magento_Customer/js/customer-data', 'mage/translate' ], function(ko, Component, customerData, $t) { 'use strict'; var cartData = customerData.get('cart')(); return Component.extend({ /** * @return {Boolean} */ isVisible: function(){ if(Object.keys(customerData.get('cart')()).length > 0){ var cartData = customerData.get('cart')(); return ko.observable(Boolean(cartData.advancecart.totals_enable)); } return ko.observable(false); }, /** * @return array */ getTotals: function () { if(Object.keys(customerData.get('cart')()).length > 0){ var cartData = customerData.get('cart')(); return cartData.advancecart.totals; } return {}; }, /** * @return {String} */ getTotalsTitle: function () { if(Object.keys(customerData.get('cart')()).length > 0){ var cartData = customerData.get('cart')(); return $t(cartData.advancecart.totals_title); } return ''; } }); });
Now as per defined in view/frontend/layout/default.xml file, we will add sliding-cart.js compponent file.
Add app/code/Magelearn/AdvanceCart/view/frontend/web/js/sliding-cart.js
define([ 'ko', 'uiComponent', 'Magento_Customer/js/customer-data', 'mage/translate' ], function(ko, Component, customerData, $t) { 'use strict'; return Component.extend({ /** * @return {Boolean} */ isTotalsEnabled: function () { if(Object.keys(customerData.get('cart')()).length > 0){ var cartData = customerData.get('cart')(); return ko.observable(Boolean(cartData.advancecart.totals_enable)); } return ko.observable(false); } }); });
define([ 'ko', 'uiComponent', 'Magento_Customer/js/customer-data', 'mage/translate' ], function(ko, Component, customerData, $t) { 'use strict'; return Component.extend({ /** * @return {String} */ getGrandTotal: function () { if(Object.keys(customerData.get('cart')()).length > 0){ var cartData = customerData.get('cart')(); return cartData.advancecart.grand_total; } return ''; } }); });
0 Comments On "Display coupon code, Upsell Products and totals information in Minicart Magneto2"