Develop Magento 2 Custom Shipping Module

Develop-Magento-2-Custom-Shipping-Module-1

Officially Magento 2 is out. Amazing news for all the techies!

Since the release of Magento 2, in the middle of November 2015, it has taken the tech world by a surprise because of its powerful features. Developers have been keeping busy in migrating their Magento 1 extension to Magento 2.

We introduce ourselves as The Brihaspati Infotech, we are an ecommerce development company with years of experience in Magento Module Development , Magento theme development and Magento 1 to Magento 2 migration services.

Have you done that yet?

Are you aware of the new concepts of Magento 2?

If not, then it will be worthwhile for you to go through our first blog on ‘What-is-new-and-how-to-migrate-for-magento-2-upgrade’

After you are well aware of the features of Magento 2, I am sure you would want to get some hands-on experience on the latest bling in technology. Thus, for all my readers I am going to explain it in easy steps how to develop a ‘Magento 2 Custom Shipping module’ that includes some basic functionality and also covers most of the developmental aspects.

 

So, read on and learn how to develop Magento 2 custom shipping module in the easiest manner!

For this, let us begin by building a simple HelloWorld shipping Module. This extension would have undefined data that shall be stored in the database. On the product page, later the values will be displayed on it.

If you are looking for more excitement, then we would also create myriad settings for changing the behavior of the extension.

Excited? Let’s Begin!

1. Configuration Creation

We would begin by installing Magento 2 and then catalog will be created where the extension files are stored. Please Note, that in Magento 2, files do not get distributed in the folders and they follow a modular structure.

The catalog containing the newly created files of the extension will be said as:

app\code\ShipHawk\Customshipping

Here ‘ShipHawk being the name of the company that has developed the extension and ‘Customshipping, the name of the Magento 2 extension that we are developing.

Now let’s begin with coding our extension. The purposes have changed slightly when we compare with the first version of Magento. As before, Extension configuration is placed in the folder named “etc”.

Now, we create app\code\ShipHawk\Customshipping\etc catalog and place module.xml.

In this file, we will set the name of the extension and its different version.

[code lang="html"]<br />
<?xml version="1.0"?><br />
 <br />
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Module/etc/module.xsd"><br />
    <module name="ShipHawk_Customshipping" setup_version="1.0.1"><br />
    </module><br />
</config><br />
[/code]

The component of registration.php will look as follows:

ShipHawk\Customshipping\registration.php

[code lang="php"]<br />
<?php

\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'ShipHawk_Customshipping',
__DIR__
);
[/code]

2. Creation of system.xml file.

app\code\ShipHawk\Customshipping\etc\adminhtml\system.xml

All the shipping methods require ‘config’ option. This enables all the system configuration fields in the admin. Also, with the help of system.xml file, shipping method options can be added.You can also add API key column in the configuration settings and manage it according to your requirements.

Store -> Settings -> Configuration -> Sales -> Shipping Methods -> Custom Shipping.

For the creation of new configuration in the admin section following code should be written:

Take a glance at the coding of the ‘System Configuration Settings’ :

[code lang="html"]<br />
<?xml version="1.0"?><br />
<!--
/**
 * @package    ShipHawk_Customshipping
 */
--><br />
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../Magento/Config/etc/system_file.xsd"><br />
    <system><br />
        </p>
<section id="carriers" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
            <group id="shiphawk_customshipping" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1"><br />
 <br />
                <label>Custom Shipping</label><br />
 <br />
                <field id="version" translate="label" type="label" sortOrder="0" showInDefault="1" showInWebsite="0" showInStore="0"><br />
                    <label>Version</label><br />
                    <frontend_model>ShipHawk\Customshipping\Block\System\Config\Form\Field\Version</frontend_model><br />
                </field><br />
 <br />
                <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0"><br />
                    <label>Enabled</label><br />
                   <source_model>Magento\Config\Model\Config\Source\Yesno</source_model><br />
                </field><br />
 <br />
                <field id="title" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1"><br />
                    <label>Title</label><br />
                </field><br />
 <br />
                <field id="api_key" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1"><br />
                    <label>API Key</label><br />
                </field><br />
 <br />
                <field id="name" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"><br />
                    <label>Method Name</label><br />
                </field><br />
 <br />
                <field id="shipping_price" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1"><br />
                    <label>Price</label><br />
                </field><br />
 <br />
                <field id="sallowspecific" translate="label" type="select" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="0"><br />
                    <label>Ship to Applicable Countries</label><br />
                    <frontend_class>shipping-applicable-country</frontend_class><br />
                   <source_model>Magento\Shipping\Model\Config\Source\Allspecificcountries</source_model><br />
                </field><br />
 <br />
                <field id="specificcountry" translate="label" type="multiselect" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="0"><br />
                    <label>Ship to Specific Countries</label><br />
                   <source_model>Magento\Directory\Model\Config\Source\Country</source_model><br />
                    <can_be_empty>1</can_be_empty><br />
                </field><br />
 <br />
                <field id="specificerrmsg" translate="label" type="textarea" sortOrder="9" showInDefault="1" showInWebsite="1" showInStore="1"><br />
                    <label>Displayed Error Message</label><br />
                </field><br />
 <br />
                <field id="showmethod" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0"><br />
                    <label>Show Method if Not Applicable</label><br />
                    <frontend_class>shipping-skip-hide</frontend_class><br />
                   <source_model>Magento\Config\Model\Config\Source\Yesno</source_model><br />
                </field><br />
 <br />
                <field id="sort_order" translate="label" type="text" sortOrder="11" showInDefault="1" showInWebsite="1" showInStore="0"><br />
                    <label>Sort Order</label><br />
                </field><br />
            </group><br />
        </section>
<p>    </system><br />
</config><br />
[/code]

3. Create Module Configuration config.xml:

Create Module Configuration config.xml file  under path app\code\ShipHawk\Customshipping\etc\config.xml:

[code lang="html"]<br />
<?xml version="1.0"?><br />
<!--
/**
 * @package    ShipHawk_Customshipping
 */
--><br />
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../Magento/Store/etc/config.xsd"><br />
    <default><br />
        <carriers><br />
            <shiphawk_customshipping><br />
                <active>0</active><br />
                <model>ShipHawk\Customshipping\Model\Carrier\Customshipping</model><br />
                <name>Fixed</name><br />
                <title>Custom Shipping</title><br />
                <price>0</price>
                <sallowspecific>0</sallowspecific><br />
                <specificerrmsg>This shipping method is not available. Please contact us, to use this method.</specificerrmsg><br />
            </shiphawk_customshipping><br />
        </carriers><br />
    </default><br />
</config></p>
<p>[/code]

4. Create a model class for the Customshipping.php:

For this, you would require creating a file on the path for instance:

app/code/ShipHawk/Customshipping/Model/Carrier/Customshipping.php

After the creation of this file, coding is done.

For this, you need to follow some significant Magento 2 rules that are required for the shipping method. Also, you will be required to properly code the php class.

All shipping class required for Magento 2 must extend as follows:

\Magento\Shipping\Model\Carrier\AbstractCarrier

and then you are required to apply “\Magento\Shipping\Model\Carrier\CarrierInterface.

Also, you are required to create two php methods in the shipping model that are “getAllowedMethods” and “collectRates“.

These two methods are required by the abstract and interface class.

Based on a configuration setting done for a shipping method, collectRatesaccepts parameter “$request” as a class instance “Magento\Quote\Model\Quote\Address\RateRequest”. This calculates and returns the shipping charges.

Finally, add  custom shipping class.

[code lang="php"]<br />
<?php

namespace ShipHawk\Customshipping\Model\Carrier;

use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\DataObject;
use Magento\Shipping\Model\Carrier\AbstractCarrier;
use Magento\Shipping\Model\Carrier\CarrierInterface;
use Magento\Shipping\Model\Config;
use Magento\Shipping\Model\Rate\ResultFactory;
use Magento\Store\Model\ScopeInterface;
use Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory;
use Magento\Quote\Model\Quote\Address\RateResult\Method;
use Magento\Quote\Model\Quote\Address\RateResult\MethodFactory;
use Magento\Quote\Model\Quote\Address\RateRequest;
use Psr\Log\LoggerInterface;
use Magento\Customer\Model\Session;
use Magento\Framework\App\RequestInterface;

class Customshipping extends AbstractCarrier implements CarrierInterface
{
/**
* Carrier's code
*
* @var string
*/
protected $_code = 'mpcustomshipping';

/**
* Whether this carrier has fixed rates calculation
*
* @var bool
*/
protected $_isFixed = true;

/**
* @var ResultFactory
*/
protected $_rateResultFactory;

/**
* @var MethodFactory
*/
protected $_rateMethodFactory;

public $_productRepo;

protected $checkoutSession;

protected $quoteIdMaskFactory;

/**
* @param ScopeConfigInterface $scopeConfig
* @param ErrorFactory $rateErrorFactory
* @param LoggerInterface $logger
* @param ResultFactory $rateResultFactory
* @param MethodFactory $rateMethodFactory
* @param array $data
*/

public function __construct(
ScopeConfigInterface $scopeConfig,
ErrorFactory $rateErrorFactory,
LoggerInterface $logger,
\Magento\Framework\App\Helper\Context $context,
\Magento\Checkout\Model\Session $checkoutSession,
\Magento\Customer\Model\Session $customerSession,
\Magento\Framework\App\Http\Context $httpContext,
\Magento\Quote\Model\QuoteIdMaskFactory $quoteIdMaskFactory,
\Magento\Store\Model\StoreManagerInterface $storeManager,
\Magento\Catalog\Model\ProductRepository $productRepo,
ResultFactory $rateResultFactory,
MethodFactory $rateMethodFactory,
array $data = []
) {
$this->_rateResultFactory = $rateResultFactory;<br />
$this->_rateMethodFactory = $rateMethodFactory;<br />
$this->_productRepo = $productRepo;<br />
$this->checkoutSession = $checkoutSession;<br />
$this->customerSession = $customerSession;<br />
$this->_storeManager = $storeManager;<br />
$this->scopeConfig = $scopeConfig;<br />
$this->quoteIdMaskFactory = $quoteIdMaskFactory;<br />
parent::__construct($scopeConfig, $rateErrorFactory, $logger, $data, $context);<br />
}</p>
<p>/**<br />
* Generates list of allowed carrier`s shipping methods<br />
* Displays on cart price rules page<br />
*<br />
* @return array<br />
* @api<br />
*/<br />
public function getAllowedMethods()<br />
{<br />
return [$this->getCarrierCode() => __($this->getConfigData('name'))];<br />
}</p>
<p>/**<br />
* Collect and get rates for storefront<br />
*<br />
* @SuppressWarnings(PHPMD.UnusedFormalParameter)<br />
* @param RateRequest $request<br />
* @return DataObject|bool|null<br />
* @api<br />
*/<br />
public function collectRates(RateRequest $request)<br />
{<br />
/**<br />
* Make sure that Shipping method is enabled<br />
*/<br />
if (!$this->isActive()) {<br />
return false;<br />
}</p>
<p>/** @var \Magento\Shipping\Model\Rate\Result $result */<br />
$result = $this->_rateResultFactory->create();</p>
<p>//$shippingPrice = $customPrice;</p>
<p>$method = $this->_rateMethodFactory->create();</p>
<p>/**<br />
* Set carrier's method data<br />
*/<br />
$method->setCarrier($this->getCarrierCode());<br />
$method->setCarrierTitle($this->getConfigData('title'));<br />
$shippingPrice = $this->getConfigData('shipping_price');</p>
<p>/**<br />
* Displayed as shipping method under Carrier<br />
*/<br />
$method->setMethod($this->getCarrierCode());<br />
$method->setMethodTitle($this->getConfigData('name'));</p>
<p>$method->setPrice($shippingPrice);<br />
$method->setCost($shippingPrice);</p>
<p>$result->append($method);</p>
<p>return $result;<br />
}</p>
<p>}<br />
[/code]

5. Create Helper class:

Create Helper class to receive the data from configuration:

ShipHawk\Customshipping\Helper\Data.php

[code lang="php"]<br />
<?php

namespace ShipHawk\Customshipping\Helper;

class Data extends \Magento\Framework\App\Helper\AbstractHelper {

    const XML_PATH_ENABLED = 'shiphawk_customshipping/general/enabled';
    const XML_PATH_DEBUG = 'shiphawk_customshipping/general/debug';

    /**
     * @var \Psr\Log\LoggerInterface
     */
    protected $_logger;

    /**
     * @var \Magento\Framework\Module\ModuleListInterface
     */
    protected $_moduleList;

    /**
     * @param \Magento\Framework\App\Helper\Context $context
     * @param \Magento\Framework\Module\ModuleListInterface $moduleList
     */
    public function __construct(
    \Magento\Framework\App\Helper\Context $context, \Magento\Framework\Module\ModuleListInterface $moduleList
    ) {
        $this->_logger = $context->getLogger();<br />
        $this->_moduleList = $moduleList;</p>
<p>        parent::__construct($context);<br />
    }</p>
<p>    /**<br />
     * Check if enabled<br />
     *<br />
     * @return string|null<br />
     */<br />
    public function isEnabled() {<br />
        return $this->scopeConfig->getValue(<br />
                        self::XML_PATH_ENABLED, \Magento\Store\Model\ScopeInterface::SCOPE_STORE<br />
        );<br />
    }</p>
<p>    public function getDebugStatus() {<br />
        return $this->scopeConfig->getValue(<br />
                        self::XML_PATH_DEBUG, \Magento\Store\Model\ScopeInterface::SCOPE_STORE<br />
        );<br />
    }</p>
<p>    public function getExtensionVersion() {<br />
        $moduleCode = 'ShipHawk_Customshipping';<br />
        $moduleInfo = $this->_moduleList->getOne($moduleCode);<br />
        return $moduleInfo['setup_version'];<br />
    }</p>
<p>    /**<br />
     *<br />
     * @param $message<br />
     * @param bool|false $useSeparator<br />
     */<br />
    public function log($message, $useSeparator = false) {<br />
        if ($this->getDebugStatus()) {<br />
            if ($useSeparator) {<br />
                $this->_logger->addDebug(str_repeat('=', 100));<br />
            }</p>
<p>            $this->_logger->addDebug($message);<br />
        }<br />
    }</p>
<p>}<br />
[/code]


6.
Create Shipping Method Version:

Create a block file Version.php under path ShipHawk\Customshipping\Block\System\Config\Form\Field\Version.php  for shipping method current version and it will be visible in the configuration settings:

[code lang="php"]<br />
<?php

namespace ShipHawk\Customshipping\Block\System\Config\Form\Field;

use Magento\Framework\Data\Form\Element\AbstractElement;

class Version extends \Magento\Config\Block\System\Config\Form\Field {

    const EXTENSION_URL = '/';

    protected $_helper;

    public function __construct(
    \Magento\Backend\Block\Template\Context $context, \ShipHawk\Customshipping\Helper\Data $helper
    ) {
        $this->_helper = $helper;<br />
        parent::__construct($context);<br />
    }</p>
<p>    protected function _getElementHtml(AbstractElement $element) {<br />
        $extensionVersion = $this->_helper->getExtensionVersion();<br />
        $extensionTitle = 'Custom Shipping';<br />
        $versionLabel = sprintf(<br />
                '<a href="%s" title="%s" target="_blank">%s</a>', self::EXTENSION_URL, $extensionTitle, $extensionVersion<br />
        );<br />
        $element->setValue($versionLabel);</p>
<p>        return $element->getValue();<br />
    }</p>
<p>}<br />
[/code]

Wooohh! You are DONE!

Now, you are only required to enable the module by running certain commands that are mentioned below:

1. php bin/magento module:enable ShipHawk_Customshipping–clear-static-content

2. php bin/magento setup:upgrade

On the Backend:

Go to System > Configuration > Sales > Shipping Methods > we can see new tab ‘Custom Shipping’ and its settings as shown below:

On the frontend:

After enabling the extension, you will see the shipping method as shown below:
Thus, successfully a very simple Magento 2 extension is created. If you follow all the steps in order, you will be able to do it without any hassles.

If you still have any doubts or you stuck up somewhere, please feel free to reach out to our team of development experts. We would help you in the best possible way.

Ellipsis-1s-200px