SHIPPING

   $rates = $quote->getShippingAddress()->getShippingRatesCollection();
   foreach ($rates as $rate)
     {
       $rate->setPrice(0.00)->save();
     }

For Returning the values you should recalculate the Rules

  $quote->getShippingAddress()->setCollectShippingRates(true)->collectShippingRates();

QUOTE ITEM

 foreach ($quote->getAllVisibleItems() as $item) {
   $item->setCustomPrice( $item->getProduct()
       ->getPrice())->setOriginalCustomPrice($item->getProduct()->getPrice())
       ->getProduct()->setIsSuperMode(false);
   $item ->getProduct()->setTaxClassId(0);
   $item->calcRowTotal();
}

Quick overview of the entity attribute system

Since the early days, Magento has offered an extensible entity attribute system based on the Entity-Attribute-Value (EAV) model.

Through this system, it’s straightforward to extend a Magento entity, with some limits:

  • only EAV entities can be extended with attributes; not all entities in a default Magento installation are EAV entities, just products, categories, customers, and customer addresses.
  • attributes can only contain scalar values, that is, booleanintfloat, or string values.

The extension attributes system, introduced in Magento 2, overcomes the above limits, making it easy to:

  • extend non-EAV entities to a condition: the entity should implement the \Magento\Framework\Api\ExtensibleDataInterface;
  • use objects to have more complex types.

Unluckily, some Magento core entities don’t implement the \Magento\Framework\Api\ExtensibleDataInterface thus they are not extensible with native extension attributes. Anyway, let’s focus on the half-full glass to explore some advantages of the extension attributes system.

What are custom attributes?

With the introduction of the extension attributes system, some new terminology comes in.

EAV attributes belong to one of the following sets:

  • system attributes – created by any default Magento installation;
  • custom attributes – created in the Admin Panel or through data patches.

So, simply put, a custom attribute is an EAV attribute created by someone else.

What are extension attributes?

We have already given a definition but let’s refresh it: an extension attribute is an attribute that extends a non-EAV entity.

But obviously, there is more.

First, the good news: an extension attribute allows us to overcome the limitations of scalar values. With an extension attribute, we can extend both EAV and non-EAV with complex objects.

For example, the gift_message extension attribute added to the order (Magento\Sales\Api\Data\OrderInterface) is of the following type:

<?php
namespace Magento\GiftMessage\Api\Data;

interface MessageInterface 
  extends \Magento\Framework\Api\ExtensibleDataInterface
{
    /**#@+
     * Constants for keys of data array. Identical to the name of the getter in snake case
     */
    const GIFT_MESSAGE_ID = 'gift_message_id';
    const CUSTOMER_ID = 'customer_id';
    const SENDER = 'sender';
    const RECIPIENT = 'recipient';
    const MESSAGE = 'message';
    /**#@-*/

    // ...
}

The price we pay for such flexibility is that extension attribute values persistency needs to be populated programmatically, whereas custom attribute values are loaded and saved automatically.

Refer to the official documentation for more details on how to add extension attributes to entity.

Let’s note that ExtensibleDataInterface doesn’t declare the methods to access extension attributes. Here is its declaration:

<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

namespace Magento\Framework\Api;

/**
 * Interface for entities which can be extended with extension attributes.
 *
 * @api
 */
interface ExtensibleDataInterface
{
    /**
     * Key for extension attributes object
     */
    const EXTENSION_ATTRIBUTES_KEY = 'extension_attributes';
}

By convention, the methods are named setExtensionAttributes() and getExtensionAttributes() but we can’t give for granted that a class implementing ExtensibleDataInterface provides those methods, so my advice is to double check it.

It’s worth mentioning that to prevent performance degradation while fetching extension attributes in collections, Magento provides a native join functionality (documented here), but this feature is limited to scalar extension attributes.

To find an example, we can look at the declaration of the is_subscribed attribute added to a customer in the module-newsletter/etc/extension_attributes.xml file:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd">
    <extension_attributes for="Magento\Customer\Api\Data\CustomerInterface">
        <attribute code="is_subscribed" type="boolean" >
            <join reference_table="newsletter_subscriber" 
                  reference_field="customer_id" 
                  join_on_field="entity_id">
                <field>subscriber_status</field>
            </join>
        </attribute>
    </extension_attributes>
</config>

💡 Let’s pay attention: for the join to work, it’s not enough to declare the above join; we also have to pass the collection to the \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface::process() method to have it populated with correct data. We can see an example in the getList() method of \Magento\Catalog\Model\ProductRepository:

<?php
namespace Magento\Catalog\Model;

// ...

class ProductRepository 
  implements \Magento\Catalog\Api\ProductRepositoryInterface
{
    // ...

    public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria)
    {
        /** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $collection */
        $collection = $this->collectionFactory->create();
        $this->extensionAttributesJoinProcessor->process($collection);
        // ..
    }

    // ...
}

Another relevant feature is that we can restrict access to the values of an entity’s extension attribute by declaring an ACL resource, as shown in the official documentation.

Where to store extension attributes?

Usually, extension attributes are non-EAV attributes, meaning that we should decide where to persist their values.

If the extension attribute is a complex object, the default choice would be to use a separate custom table.

But if the extension attribute is a scalar, extending core tables with a non-ambiguous custom column is acceptable. This way, persisting values will require less code and will be more performant because it avoids additional join conditions.

There isn’t a golden rule, though; we need to properly analyze every case before making the right decision.

Conclusions

Custom attributes are nothing new in the Magento world: just a new name given to EAV-entity attributes that Magento installation does not create.

Extension attributes, instead, are a new way to extend non-EAV entities.

With extension attributes also comes the possibility to define complex attributes, overcoming the limit of scalar types.

All this comes at a bit of cost: we have to carefully handle persistence to ensure that data is retrieved and saved the way we expect.

We often need to add more variables that we need to use on the checkout page at the time of checkout. Here is how we can do that.

The first step is to add the following code in Vendor/Module/etc/frontend/di.xml of your custom module –

<?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\Model\CompositeConfigProvider">
       <arguments>
           <argument name="configProviders" xsi:type="array">
               <item name="additional_provider" xsi:type="object">Armmage\test\Model\AdditionalConfigVars</item>
           </argument>
       </arguments>
   </type>
</config>

After this, we will create AdditionalConfigVars.php in Vendor/Module/Model and add the following code to it –

<?php

namespace Armmage\Test\Model;

use \Magento\Checkout\Model\ConfigProviderInterface

class AdditionalConfigVars implements ConfigProviderInterface
{
   public function getConfig()
   {
       $additionalVariables['test_var'] = 'Test Var';
       return $additionalVariables;
   }
}

Now all you need to do is flush the Magento cache and check  ‘window.checkoutConfig.test_var’ in your js on the checkout page. It will return ‘Test Var’.

This is all for now.

Basic module create –> link

Would you like to display another product attribute or a custom one in the checkout summary? Let’s see together how you can do that!

In our example, we will display below the product’s name, the product’s manufacturer. First of all, we have to include the manufacturer attribute in the quote item as in not included by default. So to achieve that we have to create a file under app/code/Armmage/ManufacturerAttriute/etc/catalog_attributes.xml and we add the following code.


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

As is well known, Magento 2 checkout is built up from a series of Knockout JS components which are then rendered using the Knockout JS templating system. Magento 2 defines each one of these components and their parent / child relationship in a large XML file which can be extended or overridden in your own theme or module. For Magento 2 checkout this large XML file can be found under

vendor/magento/module-checkout/view/frontend/layout/checkout_index_index.xmlThe definition of component item that is responsible for the summary item details in checkout is the following.<item name=”component” xsi:type=”string”>Armmage_ManufacturerAttriute/js/view/summary/item/details</item>So override this component we will have to create a new checkout_index_index.xml in our module, under
app/code/Armmage/ManufacturerAttriute/view/frontend/layout/checkout_index_index.xml

adding the following code which defines only the path to this component that we need to override.

<?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="checkout.root">
            <arguments>
                <argument name="jsLayout" xsi:type="array">
                    <item name="components" xsi:type="array">
                        <item name="checkout" xsi:type="array">
                            <item name="children" xsi:type="array">
                                <item name="sidebar" xsi:type="array">
                                    <item name="children" xsi:type="array">
                                        <item name="summary" xsi:type="array">
                                            <item name="children" xsi:type="array">
                                                <item name="cart_items" xsi:type="array">
                                                    <item name="children" xsi:type="array">
                                                        <item name="details" xsi:type="array">
                                                            <item name="component"
                                                                  xsi:type="string">Armmage_ManufacturerAttriute/js/view/summary/item/details</item>
                                                        </item>
                                                    </item>
                                                </item>
                                            </item>
                                        </item>
                                    </item>
                                </item>
                            </item>
                        </item>
                    </item>
                </argument>
            </arguments>
        </referenceBlock>
    </body>
</page>

As we can see on the code below the value of the component item is a path to JS file where also the template file for this component is declared. As we need to override this template file, this value has path to a JS file in our module. So you can easily copy the 

vendor/magento/module-checkout/view/frontend/web/js/view/summary/item/details.js

under

app/code/Armmage/ManufacturerAttriute/view/frontend/web/js/view/summary/item/details.js

and replace the file’s code with the code below.

*jshint browser:true jquery:true*/
/*global alert*/
define(
    [
        'uiComponent'
    ],
    function (Component) {
        "use strict";
        var quoteItemData = window.checkoutConfig.quoteItemData;
        return Component.extend({
            defaults: {
                template: 'Armmage_ManufacturerAttriute/summary/item/details'
            },
            quoteItemData: quoteItemData,
            getValue: function(quoteItem) {
                return quoteItem.name;
            },
            getManufacturer: function(quoteItem) {
                var item = this.getItem(quoteItem.item_id);
                return item.manufacturer;
            },
            getItem: function(item_id) {
                var itemElement = null;
                _.each(this.quoteItemData, function(element, index) {
                    if (element.item_id == item_id) {
                        itemElement = element;
                    }
                });
                return itemElement;
            }
        });
    }
);

Afterwards, create a html file and name it details.html under

app/code/Armmage/ManufacturerAttriute/view/frontend/web/template/summary/item/details.html

<!-- ko foreach: getRegion('before_details') -->
    <!-- ko template: getTemplate() --><!-- /ko -->
<!-- /ko -->
<div class="product-item-details">

    <div class="product-item-inner">
        <div class="product-item-name-block">
            <strong class="product-item-name" data-bind="text: $parent.name"></strong>
            <!-- ko if: (getManufacturer($parent))-->
                <strong class="product-item-manufacturer" data-bind="text: getManufacturer($parent)"></strong>
            <!-- /ko -->
            <div class="details-qty">
                <span class="label"><!-- ko i18n: 'Qty' --><!-- /ko --></span>
                <span class="value" data-bind="text: $parent.qty"></span>
            </div>
        </div>

        <!-- ko foreach: getRegion('after_details') -->
            <!-- ko template: getTemplate() --><!-- /ko -->
        <!-- /ko -->
    </div>

    <!-- ko if: (JSON.parse($parent.options).length > 0)-->
    <div class="product options" data-bind="mageInit: {'collapsible':{'openedState': 'active'}}">
        <span data-role="title" class="toggle"><!-- ko i18n: 'View Details' --><!-- /ko --></span>
        <div data-role="content" class="content">
            <strong class="subtitle"><!-- ko i18n: 'Options Details' --><!-- /ko --></strong>
            <dl class="item-options">
                <!--ko foreach: JSON.parse($parent.options)-->
                <dt class="label" data-bind="text: label"></dt>
                    <!-- ko if: ($data.full_view)-->
                    <dd class="values" data-bind="html: full_view"></dd>
                    <!-- /ko -->
                    <!-- ko ifnot: ($data.full_view)-->
                    <dd class="values" data-bind="html: value"></dd>
                    <!-- /ko -->
                <!-- /ko -->
            </dl>
        </div>
    </div>
    <!-- /ko -->
</div>

The only change to the original file, which can be found under

vendor/magento/module-checkout/view/frontend/web/template/summary/item/details.html

is that we added to display the product’s manufacturer below the product’s name.

The vendor/magento/module-checkout/Model/DefaultConfigProvider.php class is used for retrieving data in checkout. In this class, the function getConfig() is returning an array of the data which is gonna used in different places in checkout. For the checkout, the summary template is the totalsData output array field. As we can see the product’s manufacturer data is not included in that array. So we have to extend this function by creating a plugin and add the product’s manufacturer to the totalsData. After having a deeper look, we found out that this is gonna work only in the shipping step, as in the payment step, the total data array is set again only with Magento’s default quote total values. So to display the product’s manufacturer in the checkout order summary in both steps we need to add the product’s manufacturer into the quoteItemData array.

Create a di.xml file to declare the plugin.

<?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\Model\DefaultConfigProvider">
        <plugin name="checkout-summary-product-attribute" type="Armmage\ManufacturerAttriute\Plugin\Checkout\Model\DefaultConfigProvider" />
    </type>
</config>

Create a class under /app/code/Armmage/ManufacturerAttriute/Plugin/Checkout/Model/DefaultConfigProvider.php with an afterGetConfig() function, where we will add our logic of adding the product’s manufacturer information.

<?php
namespace Armmage\ManufacturerAttriute\Plugin\Checkout\Model;

use Magento\Checkout\Model\Session as CheckoutSession;

class DefaultConfigProvider
{
    /**
     * @var CheckoutSession
     */
    protected $checkoutSession;

    /**
     * Constructor
     *
     * @param CheckoutSession $checkoutSession
     */
    public function __construct(
        CheckoutSession $checkoutSession
    ) {
        $this->checkoutSession = $checkoutSession;
    }

    public function afterGetConfig(
        \Magento\Checkout\Model\DefaultConfigProvider $subject,
        array $result
    ) {
        $items = $result['totalsData']['items'];
        foreach ($items as $index => $item) {
            $quoteItem = $this->checkoutSession->getQuote()->getItemById($item['item_id']);
            $result['quoteItemData'][$index]['manufacturer'] = $quoteItem->getProduct()->getAttributeText('manufacturer');
        }
        return $result;
    }
}

So now the Attribute should be visible.

If you have any questions, just write in comments.

source



<?php

$bootstrap = realpath(__DIR__) . './app/bootstrap.php';
if (is_file($bootstrap )) {
    include $bootstrap 
}
​
$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
​
$objectManager = $bootstrap->getObjectManager();
$state = $objectManager->get('Magento\Framework\App\State');
$state->setAreaCode('adminhtml');
$productCollectionFactory = $objectManager->get('\Magento\Catalog\Model\ResourceModel\Product\CollectionFactory');
​
$collection = $productCollectionFactory->create()
->addAttributeToSelect('*')
->addAttributeToFilter('type_id', "[your type ]" );
foreach($collection  as $product){
    $product->setTypeId('simple')->save(); [changed type]
   echo "sku-> ". $product->getSku() ."\n";
}

The My Downloadable Products block has the customer-account-navigation-downloadable-products-link name:

//app/code/Magento/Downloadable/view/frontend/layout/customer_account.xml

<referenceBlock name="customer_account_navigation">
<block class="Magento\Framework\View\Element\Html\Link\Current" 
name="customer-account-navigation-downloadable-products-link">
        <arguments>
            <argument name="path"xsi:type="string">downloadable/customer/products</argument>
            <argument name="label" xsi:type="string">My Downloadable Products</argument>
        </arguments>
    </block>
</referenceBlock>

You should remove the links with help referenceBlock
You can do it in Magento_Theme/layout/default.xml

<?xml version="1.0"?>
<page xmlns:xsi="//www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">

<body>
<referenceBlock name='customer-account-navigation-downloadable-products-link' remove='true'/>
</body>
</page>

Adding a product attribute is one of the most popular operations in both Magento 1 and Magento 2. Attributes are a powerful way to solve many practical tasks related to a product.

This is quite a broad topic, but in this video we will discuss the simple process of adding a dropdown-type attribute to a product.

For this exercise, assume that the sample data set is installed.

  • We will add an attribute called clothing_material with the possible values: Cotton, Leather, Silk, Denim, Fur, and Wool.
  • We will make this attribute visible on the product view page, in bold text.
  • We will assign it to the Default attribute set and add a restriction that any “bottom” clothing, like slacks, cannot be the material Fur.

We will need to take the following steps to add the new attribute:

  1. Create a new module.
  2. Add an InstallData script.
  3. Add a source model.
  4. Add a backend model.
  5. Add a frontend model.
  6. Execute the InstallData script and verify that it works.

Let’s go through each step.

Step 1: Create a new module

As Magento is modular based, we start the process by creating a new module called Learning_ClothingMaterial.

cd <magento2_root>/app/code
mkdir Learning
mkdir Learning/ClothingMaterial

Now, create two files:

etc/module.xmlShow code

registration.phpShow code

Step 2 Create an InstallData script

Next, we need to create the InstallData script. Because adding an attribute technically adds records into several tables, such as eav_attribute and catalog_eav_attribute, this is data manipulation, not a schema change. Therefore we use InstallData instead of InstallSchema.

Create the file app/code/Learning/ClothingMaterial/Setup/InstallData.php:Show code

Let’s take a minute to look at the code.

First of all, we need to use a special setup object, not the one that comes as a parameter. This is because Catalog is an EAV entity, so to add an attribute, we have to use eavSetup rather than standard one. This holds true for any EAV entity in Magento 2 (category, product, customer, and so on).

This is why we added eavSetupFactory in a constructor.

For the install() method, all we have to do is call the addAttribute() method with 3 parameters: entity type, attribute code, and properties.

Those properties define how an attribute behaves. A full list of properties can be seen in the catalog_eav_attribute and eav_attribute tables. Note that there is a mapping between the fields in those tables and the properties in the addAttribute() method.

To see all the mappings, you should look at the \Magento\Catalog\Model\ResourceModel\Setup\PropertyMapper class.

For now, we’ll just quickly go through most important ones:

  • group: Means that we add an attribute to the attribute group “General”, which is present in all attribute sets.
  • type: varchar means that the values will be stored in the catalog_eav_varchar table.
  • label: A label of the attribute (that is, how it will be rendered in the backend and on the frontend).
  • source/frontend/backend: Special classes associated with the attribute:
    • source model: provides a list of options
    • frontend: defines how it should be rendered on the frontend
    • backend: allows you to perform certain actions when an attribute is loaded or saved. In our example, it will be validation.
  • Global: defines the scope of its values (global, website, or store)
  • visible_on_front: A flag that defines whether an attribute should be shown on the “More Information” tab on the frontend
  • is_html_allowed_on_front: Defines whether an attribute value may contain HTML

Step 3: Add a source model

Next, we need to create the source model:

app/code/Learning/ClothingMaterial/Model/Attribute/Source/Material.phpShow code

As the name implies, the getAllOptions method provides a list of all available options.

Step 4: Add a backend model

Now we will create a backend model:

app/code/Learning/ClothingMaterial/Model/Attribute/Backend/Material.phpShow code

In our example, we implement only the validate() method.

The backend model may have beforeSaveafterSave, and afterLoad methods that allow the execution of some code at the moment an attribute is saved or loaded. The backend model is what makes attribute management a really powerful method of customization.

Note that we hardcoded attributeSetId here for the sake of time. In other cases, it could be different. Make sure to check the eav_attribute_set table for the right ID.

Step 5: Add a frontend model

And finally, we create a frontend model to make our value bold:Show code

As with the backend model, this is also a very simple class.

Step 6: Execute the InstallData script and verify that it works

Now we can run our code and check the results:

cd <magento2_root>
php bin/magento setup:upgrade

After you run this, the new attribute should have been added to the database. You can check the eav_attribute and catalog_eav_attribute tables to verify that the attribute and its properties are there. We see ClothingMaterial down here, with the primary key of 155, and its corresponding entry in catalog_eav_attribute, with the primary key being 155.

Now, let’s go to the backend, open any configurable product, and we should see that clothing material dropdown. We’ll set our filters to be a configurable product with the attribute set of “bottom”.

We’ll select the first item. First, we’ll set the clothing material to Fur and attempt to save our attribute. We see that our backend model has executed successfully, so now we’ll set it to Wool and save it.

Having saved the product, we’ll now move to the frontend. It should be visible and in bold text.

Product Attribute Option Creation

A product attribute of type multiselect or select will present selectable options to the user. These options may be added manually through the admin panel or by upgrade script. The script process is slightly different depending on whether the options are being added at the moment of attribute creation or whether the options are being added at a later time to an existing attribute.

Add options to a new prouduct attribute

Basic instructions for creating a product attribute by setup or upgrade script can be found above. Before scripting the attribute creation, pick one of these two use cases for your options:

  1. You want a set of options which cannot be modified by a user through the admin panel and which can only be changed through a future code push.
  2. You want a set of options which can be modified, added or deleted through the admin panel.

For use case 1 (an ‘immutable’ set of options), follow the above instructions “Add a source model”. You will create a model that contains and dynamically returns the attribute’s selectable options to the client.

For use case 2 (a ‘mutable’ set of options), see “EAV and extension attributes”. Make sure to declare ‘Magento\Eav\Model\Entity\Attribute\Source\Table’ as the value for the ‘source’ attribute option. This ensures that Magento will store options in the appropriate database table.

With \Magento\Eav\Setup\EavSetup.php::addAttribute() and \Magento\Eav\Setup\EavSetup.php::addAttributeOptions() you can add a series of options with the following array:

1
'option' => ['values' => ['Option 1', 'Option 2', 'Option 3', etc.]];

Alternatively, you may designate a specific option sorting order as follows:

1
'option' => ['values' => [8 => 'Option 1', 3 => 'Option 2', 11 => 'Option 3', etc.]]

Add options to an existing product attribute

  • To add options to an ‘immutable’ set of options, modify the custom source model with the additional options you wish to provide.
  • Adding options to a ‘mutable’ set of options leverages the same EavSetup object as you use when creating an attribute with options, but requires an additional step because EavSetup needs to know to which attribute you want to assign new options.
  1. Assign an array of new options to a variable:
1
   $options = ['attribute_id' => null, 'values' => 'Option 1', 'Option 2', etc]];
  1. Update your array with the attribute ID from the database:
1
   $options['attribute_id'] = $eavSetup->getAttributeId($eavSetup->getEntityTypeId('catalog_product'), 'your_attribute_code');
  1. Add your options:
1
   $eavSetup->addAttributeOption($options);