19

I'm making new shipping method and I need to add new column to checkout shipping rates. Data would come from custom shipping method settings, for example method description. Or some input field where customer can add info (data probably would be saved in quote and later in order).

Probably the easiest part of all is implement template by using

Magento_Checkout/web/template/shipping.html

It just needs this

<div data-bind="text: method.description"></div>

Problem is I can't figure out how to add custom data. It's not enough to add this:

public function collectRates(RateRequest $request)
{
    if (!$this->isActive()) return false;

    $method = $this->rateMethodFactory->create();
    $method->setData('carrier', $this->getCarrierCode());
    $method->setData('carrier_title', $this->getConfigData('title'));
    $method->setData('method_title', $this->getConfigData('title'));
    $method->setData('method', $this->getCarrierCode());
    $method->setPrice($this->_price);
    $method->setData('cost', $this->_price);

    // custom
    $method->setData('description', $this->getConfigData('description'));

    $result = $this->rateResultFactory->create();
    $result->append($method);

    return $result;
}

Data for html comes from js rates(), which gets data from API:

<route url="/V1/carts/:cartId/shipping-methods" method="GET">
    <service class="Magento\Quote\Api\ShippingMethodManagementInterface" method="getList"/>
    <resources>
        <resource ref="Magento_Cart::manage" />
    </resources>
</route>

After this there are many steps while something actually gets collected. I found

Magento\Quote\Model\Cart\ShippingMethodConverter modelToDataObject()

that looked the most promising but if I try to add my attribute to it, nothing happens.

So my question is, if there actually is a way to add new data to shipping rates? In M1 it was possible. It would be crazy if M2 it wasn't possible.

There are many reasons why this should be possible. For example if I wanted to make pick up in store method with multiple stores drop down or something similar.

3
  • Hi , If you got the solution could you please share?
    – konika
    Feb 13, 2017 at 9:01
  • Well, any solution to this? Apr 25, 2017 at 4:53
  • I'm waiting this answer. May 8, 2018 at 10:44

4 Answers 4

17

The top-rated answer doesn't work because he forgot to set the "description" value inside of the \Magento\Quote\Model\Quote\Address\Rate class. If we do not create a Plugin to set the Description value on this class, then $rateModel->getMethodDescription() will always return empty. Here is a full-working version of the solution:

[Vendor]/[Module]/etc/extension_attributes.xml

<?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\Quote\Api\Data\ShippingMethodInterface">
        <attribute code="description" type="string" />
    </extension_attributes>
</config>

[Vendor]/[Module]/etc/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\Quote\Model\Cart\ShippingMethodConverter">
        <plugin name="add_description_to_method" type="<Vendor>\<module>\Plugin\Carrier\Description" disabled="false" sortOrder="30"/>
    </type>

<type name="Magento\Quote\Model\Quote\Address\Rate">
        <plugin name="add_description_to_method_rate" type="<Vendor>\<module>\Plugin\Quote\Address\Rate" disabled="false" sortOrder="3"/>
    </type>
</config>

[Vendor]/[Module]/Plugin/Carrier/Description.php

<?php

namespace Vendor\module\Plugin\Carrier;

use Magento\Quote\Api\Data\ShippingMethodInterfaceFactory;


class Description
{
    /**
     * @var ShippingMethodInterfaceFactory
     */
    protected $extensionFactory;

    /**
     * Description constructor.
     * @param ShippingMethodInterfaceFactory $extensionFactory
     */
    public function __construct(
        ShippingMethodInterfaceFactory $extensionFactory
    )
    {
        $this->extensionFactory = $extensionFactory;
    }

    /**
     * @param $subject
     * @param $result
     * @param $rateModel
     * @return mixed
     */
    public function afterModelToDataObject($subject, $result, $rateModel)
    {
        $extensionAttribute = $result->getExtensionAttributes() ?
            $result->getExtensionAttributes()
            :
            $this->extensionFactory->create()
        ;
        $extensionAttribute->setDescription($rateModel->getDescription());
        $result->setExtensionAttributes($extensionAttribute);
        return $result;
    }
}

And finally:

[Vendor]/[Module]/Plugin/Quote/Address/Rate.php

<?php
namespace <Vendor>\<Module>\Plugin\Quote\Address;

class Rate
{
    /**
     * @param \Magento\Quote\Model\Quote\Address\AbstractResult $rate
     * @return \Magento\Quote\Model\Quote\Address\Rate
     */
    public function afterImportShippingRate($subject, $result, $rate)
    {
        if ($rate instanceof \Magento\Quote\Model\Quote\Address\RateResult\Method) {
            $result->setDescription(
                $rate->getDescription()
            );
        }

        return $result;
    }
}

Do not forget to run bin/magento setup:di:compile, otherwise the extended attribute won't be generated.

You can bind the data to your template using this:

<div data-bind="text: method.extension_attributes.description"></div>

Or as a comment, like this:

<!-- ko text: $data.extension_attributes.description --><!-- /ko -->

Also do not forget to use $method->setDescription('Your Custom Description Here') or $method->setData('description', 'Your custom Description Here') inside of your custom Carrier extension (look at the original question for reference).

2
  • 3
    In latest version 2.3.4 ShippingMethodExtensionFactory need to update with ShippingMethodInterfaceFactory
    – Jack
    May 28, 2020 at 11:51
  • @Jack Good spot, I've updated the answer
    – Dan Norris
    Nov 21, 2022 at 12:26
8

You need to do this by adding description as an extension attribute as below:

/etc/extension_attributes.xml should be like this:

<?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\Quote\Api\Data\ShippingMethodInterface">
        <attribute code="method_description" type="string" />
    </extension_attributes>
</config>

In the etc/di.xml file add a plugin for overriding modelToDataObject() in Magento\Quote\Model\Cart\ShippingMethodConverter as below:

<?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\Quote\Model\Cart\ShippingMethodConverter">
        <plugin name="add_description_to_carrier" type="<Vendor>\<module>\Plugin\Carrier\Description" disabled="false" sortOrder="30"/>
    </type>
</config>

The plugin file Vendor\module\Plugin\Carrier\Description.php should be like this:

<?php

namespace Vendor\module\Plugin\Carrier;

use Magento\Quote\Api\Data\ShippingMethodExtensionFactory;

/**
 * Class Description
 * 
 */
class Description
{
    /**
     * @var ShippingMethodExtensionFactory
     */
    protected $extensionFactory;

    /**
     * Description constructor.
     * @param ShippingMethodExtensionFactory $extensionFactory
     */
    public function __construct(
        ShippingMethodExtensionFactory $extensionFactory
    )
    {
        $this->extensionFactory = $extensionFactory;
    }

    /**
     * @param $subject
     * @param $result
     * @param $rateModel
     * @return mixed
     */
    public function afterModelToDataObject($subject, $result, $rateModel)
    {
        $extensionAttribute = $result->getExtensionAttributes() ?
            $result->getExtensionAttributes()
            :
            $this->extensionFactory->create()
        ;
        $extensionAttribute->setMethodDescription($rateModel->getMethodDescription());
        $result->setExtensionAttributes($extensionAttribute);
        return $result;
    }
}

After all this you will get that description on fronend as below:

<div data-bind="text: method.extension_attributes.method_description"></div>
1
0

You need to declare the method names in interface file, the path for this file is

vendor/magento/module-quote/Api/Data/ShippingMethodInterface.php 

Example:
Declare constant at the top

const KEY_DESCRIPTION = 'description';  

then define method as follows

public function getDescription();
public function setDescription($desc);

Then you need to assign values to the following file

vendor/magento/module-quote/Model/Cart/ShippingMethod.php 

as follows

public function getDescription()
{
  return $this->_get(self::KEY_DESCRIPTION);
}
public function setDescription($desc)
{
  return $this->setData(self::KEY_DESCRIPTION, $desc);
}   
1
  • 1
    Adding method to public api (vendor/magento/module-quote/Api/Data/ShippingMethodInterface.php )??? Never do that. Apr 16, 2018 at 18:29
-1

Note: In latest version 2.3.4 ShippingMethodExtensionFactory need to update with ShippingMethodInterfaceFactory

Your Answer

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

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