13

I want to customize my minicart in Magento 2. I need to add 3 attributes: SKU, Manufacturer, and Manufacturer part number. These are existing attributes. I see where to add the output values but not where to call them from.

Matthew McLennan
  • 1,480
  • 4
  • 20
  • 40

3 Answers3

33

You can create a module to do this. It will use a plugin to add the attributes to the data array which is read by the knockout js template. Then we need to override the template to display these values.

This is the module directory:

|   registration.php
|   
+---etc
|   |   module.xml
|   |   catalog_attributes.xml
|   |   
|   \---frontend
|           di.xml
|           
+---Plugin
|       DefaultItem.php
|       
\---view
    +---frontend
    |   \---layout
    |           checkout_cart_sidebar_item_renderers.xml
    |           
    \---web
        \---template
            \---mini cart
                \---item
                        default.html

catalog_attributes.xml

<?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="manufacturer"/>
        <attribute name="part_number"/>
    </group>
</config>

di.xml

<?xml version="1.0"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Checkout\CustomerData\DefaultItem">
        <plugin name="AddAttPlug" type="Your\Module\Plugin\DefaultItem" disabled="false" sortOrder="10"/>
    </type>
</config>

DefaultItem.php

<?php

namespace Your\Module\Plugin;

use Magento\Quote\Model\Quote\Item;

class DefaultItem
{
    public function aroundGetItemData($subject, \Closure $proceed, Item $item)
    {
        $data = $proceed($item);
        $product = $item->getProduct();

        $atts = [
            "product_manufacturer" => $product->getAttributeText('manufacturer'),
            "product_part_number" => $product->getAttributeText('product_part_number')
        ];

        return array_merge($data, $atts);
    }
}

SKU already exists in data so no need to add it.

checkout_cart_sidebar_item_renderers.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="minicart">
            <arguments>
                <argument name="jsLayout" xsi:type="array">
                    <item name="components" xsi:type="array">
                        <item name="minicart_content" xsi:type="array">
                            <item name="children" xsi:type="array">
                                <item name="item.renderer" xsi:type="array">
                                    <item name="config" xsi:type="array">
                                        <item name="template" xsi:type="string">Your_Module/minicart/item/default</item>
                                    </item>
                                </item>
                            </item>
                        </item>
                    </item>
                </argument>
            </arguments>
        </referenceBlock>
    </body>
</page>

default.html is a copy of Magento/Checkout/view/frontend/web/template/minicart/item/default.html with changes made at line 66

<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}, text: 
                  product_name"></a>
                <!-- /ko -->
                <!-- ko ifnot: product_has_url -->
                    <!-- ko text: product_name --><!-- /ko -->
                <!-- /ko -->
            </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: option.value.join('<br>')"></span>
                            <!-- /ko -->
                            <!-- ko ifnot: Array.isArray(option.value) -->
                                <span data-bind="html: 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 data-bind="html: 'SKU#: ' + item.product_sku"></div>
                <div data-bind="html: 'Manufacturer: ' + item.product_manufacturer"></div>
                <div data-bind="html: 'Part #: ' + item.product_part_number"></div>

                <div class="details-qty qty">
                    <label class="label" data-bind="i18n: 'Qty', attr: {
                           for: 'cart-item-'+item_id+'-qty'}"></label>
                    <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 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>
</li>
Ashish Ramchandani
  • 820
  • 1
  • 7
  • 22
Aaron Allen
  • 9,009
  • 2
  • 26
  • 36
  • What is in registration.php and module.xml? – Matthew McLennan Sep 02 '16 at 12:52
  • I added this and now my output states "UNDEFINED." Does that mean its working but I have my attributes are misnamed? – Matthew McLennan Sep 02 '16 at 13:33
  • Registration.php and module.xml are boilerplate files, you should check out this tutorial http://alanstorm.com/magento_2_mvvm_mvc to get a handle on how m2 modules are defined. Could you maybe post a screenshot of the error your getting or else be more specific? – Aaron Allen Sep 02 '16 at 20:08
  • @AaronAllen, Its work for me, but I also want to override the content.html of the same minicart and want to add some dynamic data in that file. Can you please explain how I can achieve this. Thanks in advance ! – Ashish Jagnani Sep 05 '16 at 13:32
  • @AshishJagnani this might help you: http://magento.stackexchange.com/questions/127132/how-do-i-add-a-custom-field-to-the-cart-in-magento-2/127143#127143 – Aaron Allen Sep 05 '16 at 19:10
  • 2
    Instead if loading the attributes in aroundGetItemData() it is possible to add them to etc/catalog_attributes.xml. Having done that the attributes can be accessed with $item->getProduct()->getAttributeText('attribute_code'); _productRepo is no longer needed. – Andreas Riedmüller Sep 28 '17 at 11:49
  • <plugin name="AddAttPlug" does this need to be changed to DefaultItem? I followed the steps but it's now working correctly for me. – getData Apr 10 '18 at 16:55
  • thanks for the solution but it's not working for me...getting 'false' as my custom-prod-value instead of original one...please help me. – Wakar Ahamad Apr 02 '20 at 09:43
  • In order to make it work for me I had to put the html file in this folder Custom_Module/view/frontend/web/template/... So the web folder needed to be inside of the frontend folder. – Andreas Daoutis Jun 07 '20 at 18:30
  • @AaronAllen hi, i tried your solution, but i am getting "undefined" instead of attribute value. can you please help me? – quickshiftin Mar 24 '21 at 05:28
  • @MatthewMcLennan i am also getting "UNDEFINED". have you found any solution for that? please help. – quickshiftin Mar 24 '21 at 05:31
0

i have slove my query by 2 simple step in magento 2.1:

Change into file :->

1.DefaultItem.php Add line :

    $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
    $this->product = $objectManager->get('Magento\Catalog\Model\Product')-
    >load($this->item->getId());

   *Add element into return array result:*

   'short_description' => $this->product->getShortDescription(),

2.default.html Add line:

   <a data-bind="text: short_description"></a>

i hope this will help you.

0

I couldn't find a complete answer that worked for me, below is working for M2.4.4.

Module structure:

registration.php
composer.json
Plugin
└─ Checkout
   └─ CustomerData
      └─ DefaultItemPlugin.php
etc
├─ module.xml
├─ catalog_attributes.xml
└─ frontend
   └─ di.xml
view
└─ frontend
   ├─ layout
   │  └─ checkout_cart_sidebar_item_renderers.xml
   └─ web
      └─ template
         └─ minicart
            └─ item
               └─ default.html

module.xml:

<?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="Vendor_Module" setup_version="0.0.1">
    </module>
</config>

registration.php:

<?php
/**
 * Copyright ©  All rights reserved.
 * See COPYING.txt for license details.
 */
use Magento\Framework\Component\ComponentRegistrar;

ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Vendor_Module', DIR);

composer.json:

{
    "name": "vendor/module-name",
    "description": "Magento 2 module to get attribute in minicart template.",
    "type": "magento2-module",
    "license": "proprietary",
    "authors": [
        {
            "name": "Vendor",
            "email": "example@email.com"
        }
    ],
    "minimum-stability": "dev",
    "require": {},
    "autoload": {
        "psr-4": {
            "Vendor\\Module\\": ""
        },
        "files": [
            "registration.php"
        ]
    }
}

catalog_attributes.xml:

<?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="brand"/>
    </group>
</config>

di.xml:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Checkout\CustomerData\AbstractItem">
        <plugin name="Vendor_Module::add-custom-attribute-to-cart-minicart" type="Vendor\Module\Plugin\Checkout\CustomerData\DefaultItemPlugin" sortOrder="10" disabled="false" />
    </type>
</config>

DefaultItemPlugin.php: (credit: https://magento.stackexchange.com/a/309482/32939)

<?php
declare(strict_types=1);

namespace Vendor\Module\Plugin\Checkout\CustomerData;

use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Checkout\CustomerData\DefaultItem;

class DefaultItemPlugin { /** * @var \Magento\Catalog\Api\ProductRepositoryInterface */ private $productRepository;

public function __construct(
    ProductRepositoryInterface $productRepository
) {
    $this-&gt;productRepository = $productRepository;
}

public function afterGetItemData(
    DefaultItem $subject,
    $result
) {
    $product = $this-&gt;productRepository-&gt;get($result['product_sku']);

    return \array_merge(
        ['brand' =&gt; $product-&gt;getData('brand')],
        $result
    );
}

}

checkout_cart_sidebar_item_renderers.xml:

<?xml version="1.0"?>
<!--
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="minicart">
            <arguments>
                <argument name="jsLayout" xsi:type="array">
                    <item name="components" xsi:type="array">
                        <item name="minicart_content" xsi:type="array">
                            <item name="children" xsi:type="array">
                                <item name="item.renderer" xsi:type="array">
                                    <item name="config" xsi:type="array">
                                        <item name="displayArea" xsi:type="string">defaultRenderer</item>
                                        <item name="template" xsi:type="string">Vendor_Module/minicart/item/default</item>
                                    </item>
                                </item>
                            </item>
                        </item>
                    </item>
                </argument>
            </arguments>
        </referenceBlock>
    </body>
</page>

default.html: (line 33 - 35)

<!--
/**
 * 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 -->
    &lt;div class=&quot;product-item-details&quot;&gt;
        &lt;strong class=&quot;product-item-name&quot;&gt;
            &lt;!-- ko if: product_has_url --&gt;
            &lt;a data-bind=&quot;attr: {href: product_url}, html: $parent.getProductNameUnsanitizedHtml(product_name)&quot;&gt;&lt;/a&gt;
            &lt;!-- /ko --&gt;
            &lt;!-- ko ifnot: product_has_url --&gt;
                &lt;span data-bind=&quot;html: $parent.getProductNameUnsanitizedHtml(product_name)&quot;&gt;&lt;/span&gt;
            &lt;!-- /ko --&gt;

            &lt;!-- ko if: brand --&gt;
                &lt;span data-bind=&quot;text: brand&quot;&gt;&lt;/span&gt;
            &lt;!-- /ko --&gt;
        &lt;/strong&gt;

        &lt;!-- ko if: options.length --&gt;
        &lt;div class=&quot;product options&quot; data-mage-init='{&quot;collapsible&quot;:{&quot;openedState&quot;: &quot;active&quot;, &quot;saveState&quot;: false}}'&gt;
            &lt;span data-role=&quot;title&quot; class=&quot;toggle&quot;&gt;&lt;!-- ko i18n: 'See Details' --&gt;&lt;!-- /ko --&gt;&lt;/span&gt;

            &lt;div data-role=&quot;content&quot; class=&quot;content&quot;&gt;
                &lt;strong class=&quot;subtitle&quot;&gt;&lt;!-- ko i18n: 'Options Details' --&gt;&lt;!-- /ko --&gt;&lt;/strong&gt;
                &lt;dl class=&quot;product options list&quot;&gt;
                    &lt;!-- ko foreach: { data: options, as: 'option' } --&gt;
                    &lt;dt class=&quot;label&quot;&gt;&lt;!-- ko text: option.label --&gt;&lt;!-- /ko --&gt;&lt;/dt&gt;
                    &lt;dd class=&quot;values&quot;&gt;
                        &lt;!-- ko if: Array.isArray(option.value) --&gt;
                            &lt;span data-bind=&quot;html: $parents[1].getOptionValueUnsanitizedHtml(option.value.join('&lt;br&gt;'))&quot;&gt;&lt;/span&gt;
                        &lt;!-- /ko --&gt;
                        &lt;!-- ko if: (!Array.isArray(option.value) &amp;&amp; ['file', 'html'].includes(option.option_type)) --&gt;
                            &lt;span data-bind=&quot;html: $parents[1].getOptionValueUnsanitizedHtml(option.value)&quot;&gt;&lt;/span&gt;
                        &lt;!-- /ko --&gt;
                        &lt;!-- ko if: (!Array.isArray(option.value) &amp;&amp; !['file', 'html'].includes(option.option_type)) --&gt;
                        &lt;span data-bind=&quot;text: option.value&quot;&gt;&lt;/span&gt;
                        &lt;!-- /ko --&gt;
                    &lt;/dd&gt;
                    &lt;!-- /ko --&gt;
                &lt;/dl&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;!-- /ko --&gt;

        &lt;div class=&quot;product-item-pricing&quot;&gt;
            &lt;!-- ko if: canApplyMsrp --&gt;

            &lt;div class=&quot;details-map&quot;&gt;
                &lt;span class=&quot;label&quot; data-bind=&quot;i18n: 'Price'&quot;&gt;&lt;/span&gt;
                &lt;span class=&quot;value&quot; data-bind=&quot;i18n: 'See price before order confirmation.'&quot;&gt;&lt;/span&gt;
            &lt;/div&gt;
            &lt;!-- /ko --&gt;
            &lt;!-- ko ifnot: canApplyMsrp --&gt;
            &lt;!-- ko foreach: $parent.getRegion('priceSidebar') --&gt;
                &lt;!-- ko template: {name: getTemplate(), data: item.product_price, as: 'price'} --&gt;&lt;!-- /ko --&gt;
            &lt;!-- /ko --&gt;
            &lt;!-- /ko --&gt;

            &lt;div class=&quot;details-qty qty&quot;&gt;
                &lt;label class=&quot;label&quot; data-bind=&quot;i18n: 'Qty', attr: {
                       for: 'cart-item-'+item_id+'-qty'}&quot;&gt;&lt;/label&gt;
                &lt;input data-bind=&quot;attr: {
                       id: 'cart-item-'+item_id+'-qty',
                       'data-cart-item': item_id,
                       'data-item-qty': qty,
                       'data-cart-item-id': product_sku
                       }, value: qty&quot;
                       type=&quot;number&quot;
                       size=&quot;4&quot;
                       class=&quot;item-qty cart-item-qty&quot;/&gt;
            &lt;button data-bind=&quot;attr: {
                       id: 'update-cart-item-'+item_id,
                       'data-cart-item': item_id,
                       title: $t('Update')
                       }&quot;
                        class=&quot;update-cart-item&quot;
                        style=&quot;display: none&quot;&gt;
                    &lt;span data-bind=&quot;i18n: 'Update'&quot;&gt;&lt;/span&gt;
                &lt;/button&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;div class=&quot;product actions&quot;&gt;
            &lt;!-- ko if: is_visible_in_site_visibility --&gt;
            &lt;div class=&quot;primary&quot;&gt;
                &lt;a data-bind=&quot;attr: {href: configure_url, title: $t('Edit item')}&quot; class=&quot;action edit&quot;&gt;
                    &lt;span data-bind=&quot;i18n: 'Edit'&quot;&gt;&lt;/span&gt;
                &lt;/a&gt;
            &lt;/div&gt;
            &lt;!-- /ko --&gt;
            &lt;div class=&quot;secondary&quot;&gt;
                &lt;a href=&quot;#&quot; data-bind=&quot;attr: {'data-cart-item': item_id, title: $t('Remove item')}&quot;
                   class=&quot;action delete&quot;&gt;
                    &lt;span data-bind=&quot;i18n: 'Remove'&quot;&gt;&lt;/span&gt;
                &lt;/a&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;message notice&quot; if=&quot;$data.message&quot;&gt;
    &lt;div data-bind=&quot;text: $data.message&quot;&gt;&lt;/div&gt;
&lt;/div&gt;

</li>

alexwatever
  • 189
  • 2
  • 12