4

we are using Magento V 2.3.5-p1

In default Magento when we select options on the bundle product page the selected option will be displayed in the summary section. By default, only the quantity and name of the selected option are displayed in the summary section.

I have created one bundle product and created bundle options with input type radio buttons and added products in that bundle option. I have fetched and displayed product images (which were added as bundle options) on the front end.

what I want to do is display the product image with the product name when the user selects an option in the bundle product. I want to display images in the summary section.

I had noticed that the summary section is based on x-Magento-template. So How can I display product images in the summary section? Is there a way to display the product images in x-Magento-template?

1
  • Yes, you need to override the magentio default files and added your custom code there into your extension. Sep 6, 2021 at 5:37

1 Answer 1

0

so here I was facing same problem as yours after ages !!! finally I solved it.

(1) Actually on summary.phtml file data is rendered using this.cache.currentElement All the Product data coming from it and it has Product name [label] & Price in itself.

(2) For Price we have to add priceUtils function in product-summary.js.

(3) For Product Image we have to UpdateElement of this.cache.currentElement.

So, let's Do it !!!

--> Find summary.phtml in your core files : vendor/magento/module-bundle/view/frontend/templates/catalog/product/view/summary.phtml

override in your custom module : app/code/VendorName/ModuleName/view/frontend/templates/catalog/product/view/summary.phtml

we have to modify & add some extra codes here in our custom file.

Step 1 : here we have to get Parent Product ID as well as based on that we have to get Child Product Data. for that I simply Extend Core Block of summary.phtml which is "Magento\Catalog\Block\Product\View".

Create di.xml for it like this :

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Magento\Catalog\Block\Product\View" 
                type="VendorName\ModuleName\Block\Product\View" />
</config>

Now Create Custom Block VendorName\ModuleName\Block\Product View.php like this :

<?php

namespace VendorName\ModuleName\Block\Product;

use Magento\Catalog\Block\Product\View as MagentoProductView;
use Magento\Catalog\Model\Product;

class View extends MagentoProductView
{
    /**
     * Custom function to retrieve product data
     *
     * @return array
     */
    public function getCustomFunction(Product $product)
    {
        $parentId = $product->getTypeId();
        
        return $parentId; 
    }

    public function getProductData()
    {
        $product = $this->getProduct();
        $productData = [
            'name' => $product->getName(),
            'sku' => $product->getSku(),
        ];
        return $productData;
    }
    
    public function getChildImagesByParentId($parentId)
    {
        $childData = [];
        $product = $this->getProduct();
        $objectManager = \Magento\Framework\App\ObjectManager::getInstance();

        if ($product->getTypeId() === 'bundle') {
            $selectionCollection = $product->getTypeInstance()->getSelectionsCollection(
                $product->getTypeInstance()->getOptionsIds($product),
                $product
            );
            foreach ($selectionCollection as $option) {
                if ($option['parent_product_id'] == $product->getId()) {
                    $ChildName = $option->getName();
                    $childProduct = $objectManager->create('Magento\Catalog\Model\Product')->load($option->getProductId());
                    $childImage = $childProduct->getImage();
                    $childImage = $this->_storeManager->getStore()->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA) . 'catalog/product' . $childImage;
                    $childData[] = [
                        'name' => $ChildName,
                        'image' => $childImage,
                    ];
                }
            }
        }

        return $childData;
    }
}

Step 2 : Custom summary.phtml as below :

<?php
$_product = $block->getProduct();
$ParentSku = $block->getCustomFunction($_product);
$ChildData = $block->getChildImagesByParentId($ParentSku);
?>


<?php if ($_product->isSaleable() && $block->hasOptions()) : ?>
<div id="bundleSummary"
     class="block-bundle-summary"
     data-mage-init='{"sticky":{"container": ".product-add-form"}}'>
    <div class="title">
        <strong><?= $block->escapeHtml(__('Your Customization')) ?></strong>
    </div>
    <div class="content">
        <div class="bundle-info">
            <?= $block->getImage($_product, 'bundled_product_customization_page')->toHtml() ?>
            <div class="product-details">
                <strong class="product name"><?= $block->escapeHtml($_product->getName()) ?></strong>
                <?php if ($_product->getIsSalable()) : ?>
                    <p class="available stock" title="<?= $block->escapeHtmlAttr(__('Availability')) ?>">
                        <span><?= $block->escapeHtml(__('In stock')) ?></span>
                    </p>
                <?php else : ?>
                    <p class="unavailable stock" title="<?= $block->escapeHtmlAttr(__('Availability')) ?>">
                        <span><?= $block->escapeHtml(__('Out of stock')) ?></span>
                    </p>
                <?php endif; ?>
                <?= $block->getChildHtml('product.price.render.bundle.customization', true) ?>
            </div>
        </div>
       <div class="bundle-summary">
            <strong class="subtitle"><?= $block->escapeHtml(__('Summary')) ?></strong>
            <div id="bundle-summary" data-container="product-summary">
                <ul data-mage-init='{"productSummary": []}' class="bundle items"></ul>
                <script data-template="bundle-summary" type="text/x-magento-template">
                    <li>
                        <strong class="label"><%- data._label_ %>:</strong>
                        <div data-container="options">
                            <% _.each(data._selected_options_, function(option) { %>
                                <div>
                                    <%= option.label %>
                                    <span class="option-price">+ <%= option.price %></span>
                                </div>
                            <% }); %>
                        </div>
                    </li>
                </script>
                <script data-template="bundle-option" type="text/x-magento-template">
                    <div class="child-image">
                        <% _.each(childData, function(item) { %>
                            <% if (item.name === data._label_) { %>
                                <img src="<%= item.image %>" alt="<%= item.name %>" class="bundle-image">
                            <% } %>
                        <% }); %>
                    </div>
                    <div><?= /* @noEscape */ __('%1 x %2', '<%- data._quantity_ %>', '<%- data._label_ %>') ?></div>
                    <div class="total_price"><%- data._option_total_ %></div>
                    
                </script>
               
            </div>
        </div>

        <?= $block->getChildHtml('addtocart', true) ?>
    </div>
</div>
<style>
    .child-image {
    width: 20% !important;
    margin-top: 15px !important;
}
</style>
<script>
    var childData = <?php echo json_encode($ChildData); ?>;
</script>

<script type="text/x-magento-init">
    {
        ".product-add-form": {
            "slide": {
                "slideSpeed": 1500,
                "slideSelector": "#bundle-slide",
                "slideBackSelector": ".action.customization.back",
                "bundleProductSelector": "#bundleProduct",
                "bundleOptionsContainer": ".product-add-form"
                <?php if ($block->isStartCustomization()) : ?>
                ,"autostart": true
                <?php endif;?>
            }
        }
    }
</script>
<?php endif; ?>


Now template file looking Perfect!!!

We have to move on product-summary.js for render Extra Elements of our [ProductPrice,Product Images,etc]

so go to this path : vendor/magento/module-bundle/view/frontend/web/js/product-summary.js Now we have to ovveride this js file and for that we have to create requirejs-config.js.

Create app/code/VendorName/ModuleName/view/frontend/requirejs-config.js like this :

var config = {
    map: {
        '*': {
            'Magento_Bundle/js/product-summary': 'VendorName_ModuleName/js/product-summary-override'
        }
    }
};

Now last but not least we have to create product-summary-override.js on this path : app/code/VendorName/ModuleName/view/frontend/web/js/product-summary-override.js like this :

/**
 * Copyright © 2016 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
define([
    'jquery',
    'mage/template',
    'priceUtils', // add here priceUtils as dependency to format price
    'jquery/ui',
    'Magento_Bundle/js/price-bundle'
], function ($, mageTemplate, priceUtils) { // add priceUtils as paramater
    'use strict';

    /**
     * Widget product Summary:
     * Handles rendering of Bundle options and displays them in the Summary box
     */
    $.widget('mage.productSummary', {
        options: {
            mainContainer:          '#product_addtocart_form',
            templates: {
                summaryBlock:       '[data-template="bundle-summary"]',
                optionBlock:        '[data-template="bundle-option"]',
                image:              '[data-template="bundle-image"]'
            },
            optionSelector:         '[data-container="options"]',
            summaryContainer:       '[data-container="product-summary"]',
            bundleSummaryContainer: '.bundle-summary'
        },
        cache: {},
        /**
         * Method attaches event observer to the product form
         * @private
         */
        _create: function () {
            this.element
                .closest(this.options.mainContainer)
                .on('updateProductSummary', $.proxy(this._renderSummaryBox, this))
                .priceBundle({})
            ;
        },
        /**
         * Method extracts data from the event and renders Summary box
         * using jQuery template mechanism
         * @param {Event} event
         * @param {Object} data
         * @private
         */
        _renderSummaryBox: function (event, data) {
            this.cache.currentElement = data.config;
            this.cache.currentElementCount = 0;

            // Clear Summary box
            this.element.html('');

            $.each(this.cache.currentElement.selected, $.proxy(this._renderOption, this));
            this.element
                .parents(this.options.bundleSummaryContainer)
                .toggleClass('empty', !this.cache.currentElementCount); // Zero elements equal '.empty' container
        },

        /**
         * @param {String} key
         * @param {String} row
         * @private
         */
        _renderOption: function (key, row) {
            var template;

            if (row && row.length > 0 && row[0] !== null) {

                var basePrice = 0;

              // start calculate option total price
                var widget = this;
                row.forEach(function(item){
                    var selection = widget.cache.currentElement.options[key].selections[item];
                    basePrice += selection.prices.finalPrice.amount; // add the base price of each selected option
                });
                    this.cache.currentElement.options[key].basePrice = priceUtils.formatPrice(basePrice, this.cache.currentElement.priceFormat.pattern);

                template = this.element
                    .closest(this.options.summaryContainer)
                    .find(this.options.templates.summaryBlock)
                    .html();
                template = mageTemplate($.trim(template), {
                    data: {
                        _label_: this.cache.currentElement.options[key].title,
                        _option_total_: this.cache.currentElement.options[key].basePrice
                    }
                });
                

                this.cache.currentKey = key;
                this.cache.summaryContainer = $(template);
                this.element.append(this.cache.summaryContainer);

                $.each(row, function(index, item) {
                var option = widget.cache.currentElement.options[key].selections[item];
                option.basePrice = priceUtils.formatPrice(option.prices.finalPrice.amount, widget.cache.currentElement.priceFormat.pattern); // save base price in option object
                option.labelWithBasePrice = option.label + ' + ' + option.basePrice; // add base price to the label
                widget._renderOptionRow(index, item);
        }); 
                this.cache.currentElementCount += row.length;

                //Reset Cache
                this.cache.currentKey = null;
            }
        },

        /**
         * @param {String} key
         * @param {String} optionIndex
         * @private
         */
        _renderOptionRow: function (key, optionIndex) {
            var template;

            template = this.element
                .closest(this.options.summaryContainer)
                .find(this.options.templates.optionBlock)
                .html();
            template = mageTemplate($.trim(template), {
                data: {
                    _quantity_: this.cache.currentElement.options[this.cache.currentKey].selections[optionIndex].qty,
                    _label_: this.cache.currentElement.options[this.cache.currentKey].selections[optionIndex].name,
                    _option_total_: priceUtils.formatPrice(this.cache.currentElement.options[this.cache.currentKey].selections[optionIndex].prices.finalPrice.amount * this.cache.currentElement.options[this.cache.currentKey].selections[optionIndex].qty, this.cache.currentElement.priceFormat.pattern) // row price calculation
                }
            });
            this.cache.summaryContainer
                .find(this.options.optionSelector)
                .append(template);
        },
        updateElement: function(element) {
            var $element = $(element),
                optionId = $element.attr('data-option-id'),
                optionValue = $element.attr('data-option-value'),
                optionQty = $element.attr('data-option-qty'),
                optionPrice = $element.attr('data-option-price'),
                optionLabel = $element.parents('.product-options-wrapper').find('.product-options-label[for="' + optionId + '"]').text(),
                imageSrc = $element.parents('.bundle-option').find('.bundle-image').attr('src'); // Get the image src from the DOM
                console.log("imageSrc",imageSrc);
            this.cache.currentElement = {
                id: optionId,
                value: optionValue,
                qty: optionQty,
                price: optionPrice,
                label: optionLabel,
                image: imageSrc // Add the image src to the current element
            };
            this.cache.selectedOptions[optionId] = {
                value: optionValue,
                qty: optionQty,
                price: optionPrice,
                label: optionLabel,
                image: imageSrc // Add the image src to the selected options
            };
        }

    });

    return $.mage.productSummary;
});


Code Part Completed !!!

Now Run all the Required cmd commands and show frontend :

Before : enter image description here

After : enter image description here

Just Understand my Steps Carefully and ping me if you got any trouble !!!

Happy To Help you

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.