Magento 2 – Disable Shipping Method at Checkout Based on Product’s Custom Attribute.

Today, I am going to show how you can disable the free shipping method based on product attribute (no_free_shipping) in the checkout page.

To accomplish this solution I will use Magento 2 Plugin (Interceptor) system and rewrite the logic into collectCarrierRates method (class Magento\Shipping\Model\Shipping) by using around plugin system. So our plugin method name will be aroundCollectCarrierRates. If you don’t know about the Magento 2 plugin system please check on their official documentation for further information.

Let’s create a product attribute named no_free_shipping.

Create a basic module (Milandev_ShippingHide) by adding registration.php and module.xml files.
app/code/Milandev/ShippingHide/etc/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="Milandev_ShippingHide" setup_version="1.0.0">
		<sequence>
			<module name="Magento_Shipping"/>
		</sequence>
	</module>
</config>

app/code/Milandev/ShippingHide/registration.php

<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Milandev_ShippingHide',
    __DIR__
);

Declare the plugin class for core Magento\Shipping\Model\Shipping in the following file.
app/code/Milandev/ShippingHide/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\Shipping\Model\Shipping">
		<plugin disabled="false" name="Milandev_ShippingHide_Plugin_Magento_Shipping_Model_Shipping" sortOrder="10" type="Milandev\ShippingHide\Plugin\Magento\Shipping\Model\Shipping"/>
	</type>
</config>

This is the final plugin class, which return false for free shipping method, while cart products have no_free_shipping is selected to yes.
app/code/Milandev/ShippingHide/Plugin/Magento/Shipping/Model/Shipping.php

<?php
namespace Milandev\ShippingHide\Plugin\Magento\Shipping\Model;

class Shipping
{
    protected $product;

    public function __construct(
        \Magento\Catalog\Model\ProductFactory $product
    ) {
        $this->product = $product; 
    }

    public function aroundCollectCarrierRates(
        \Magento\Shipping\Model\Shipping $subject,
        \Closure $proceed,
        $carrierCode,
        $request
    ) {
        $noFreeShipping = false;
        $allItems = $request->getAllItems();
        
        // iterate all cart products to check if no_free_shipping is true
        foreach ($allItems as $item) {    
            $_product = $this->product->create()->load($item->getProduct()->getId());
            // if product has no_free_shipping true
            if ($_product->getNoFreeShipping()) {
                $noFreeShipping = true;
                break;
            }
        }
        // if no_free_shipping is yes and shipping method free shipping return nothing
        if ($noFreeShipping && $carrierCode == 'freeshipping') {
            return false;
        }

        $result = $proceed($carrierCode, $request);
        return $result;
    }
}

That’s it.