Magento – Make Magento 2 Product Attribute Read-Only

eavmagento2productproduct-attribute

In Magento 1, we used the lockAttribute($attributeCode) method from a catalog_product_edit_action observer, and it prevented the admin user from editing that particular attribute. See Read-Only Product Backend attribute.

Magento 2 has the same lockAttribute method in Magento\Catalog\Model\AbstractModel. However, the UI for the attribute is no different after calling this method.

I also tried it on catalog_product_save_before, and it still allowed me to apply a new value to the attribute.

Vendor/Module/Observer/Lock.php

namespace Vendor\Module\Observer;

class Lock implements \Magento\Framework\Event\ObserverInterface
{
    /**
     * Observer to catalog_product_save_before to lock attributes.
     *
     * @param \Magento\Framework\Event\Observer $observer
     */
    public function execute(\Magento\Framework\Event\Observer $observer)
    {
        $product = $observer->getEvent()->getProduct();
        $product->lockAttribute('my_custom_attribute');
    }
}

Vendor/Module/etc/adminhtml/events.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <!-- <event name="catalog_product_save_before"> -->
    <event name="catalog_product_edit_action">
        <observer name="test_lock_attributes" instance="Vendor\Module\Observer\Lock"/>
    </event>
</config>

Best Answer

Starting from 2.1 Magento uses UI component to build product admin form. Thus, it should be customized as suggested in the manual.

In particular, your task could be accomplished with modifier approach.

Create DI file under YourVendor/YourModule/etc/adminhtml/di.xml with the content like:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <virtualType name="Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Pool">
        <arguments>
            <argument name="modifiers" xsi:type="array">
                <item name="sap_product" xsi:type="array">
                    <item name="class" xsi:type="string">YourVendor\YourModule\Ui\DataProvider\Product\Form\Modifier\Attributes</item>
                    <item name="sortOrder" xsi:type="number">1000</item>
                </item>
            </argument>
        </arguments>
    </virtualType>
</config>

Create modifier class:

<?php
namespace YourVendor\YourModule\Ui\DataProvider\Product\Form\Modifier;

use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier;
use Magento\Framework\Stdlib\ArrayManager;

class Attributes extends AbstractModifier
{
    private $arrayManager;

    public function __construct(ArrayManager $arrayManager)
    {
        $this->arrayManager = $arrayManager;
    }

    public function modifyData(array $data)
    {
        return $data;
    }

    public function modifyMeta(array $meta)
    {
        $attribute = '...'; // Your attribute code goes here

        $path = $this->arrayManager->findPath($attribute, $meta, null, 'children');
        $meta = $this->arrayManager->set(
            "{$path}/arguments/data/config/disabled",
            $meta,
            true
        );
        return $meta;
    }
}

modifyMeta method allows you to dynamically customise UI component's configuration. Here we find the attribute element configuration and update it's disabled flag to true.