How to build, use, and manipulate custom extension attributes in Magento 2

In Magento 2, extension attributes allow developers to add custom fields or properties to existing entities without modifying their core code. This is useful when you need to store additional information related to an entity that is not already available in the system.

Here are some benefits of using extension attributes in Magento 2:

  1. Easy customization: By using extension attributes, developers can easily add or remove custom fields to existing entities without modifying the core code. This makes customization of the system much easier and reduces the risk of conflicts with future updates.
  2. Improved scalability: With extension attributes, it is easier to scale your Magento 2 store to meet the needs of your business. You can add new custom fields as your business grows without worrying about performance issues or database constraints.
  3. Better data management: Extension attributes allow you to store additional data related to an entity in a structured manner. This makes it easier to manage and retrieve data when needed.
  4. Improved module compatibility: By using extension attributes, you can improve module compatibility with other third-party modules. This is because extensions can use extension attributes to interact with each other without modifying the core code.
  5. Future-proofing: Using extension attributes future-proofs your customizations. This is because extension attributes are less likely to break when new updates or versions of Magento 2 are released.

Overall, extension attributes are a powerful feature in Magento 2 that allow for easy customization, improved scalability, better data management, improved module compatibility, and future-proofing of customizations.

Example of add a scalar and non-scalar extension attribute

A scalar extension attribute is a simple attribute that can hold a single value, such as a string or an integer. To add a scalar extension attribute to an entity in Magento 2, follow these steps:

  • Create an interface for the extension attribute:
<?php
namespace Vendor\Module\Api\Data;

interface OrderExtensionInterface extends \Magento\Framework\Api\ExtensionAttributesInterface
{
    const CUSTOM_ATTRIBUTE = 'custom_attribute';

    /**
     * @return string|null
     */
    public function getCustomAttribute();

    /**
     * @param string $value
     * @return $this
     */
    public function setCustomAttribute($value);
}

In this example, we have created an interface called OrderExtensionInterface that extends the ExtensionAttributesInterface. We have also defined a constant called CUSTOM_ATTRIBUTE that represents the name of our extension attribute. Finally, we have defined two methods, getCustomAttribute() and setCustomAttribute(), which allow us to get and set the value of our extension attribute, respectively.

  • Implement the interface in the entity model:
<?php
namespace Vendor\Module\Model\Data;

use Vendor\Module\Api\Data\OrderExtensionInterface;

class OrderExtension extends \Magento\Framework\Api\AbstractSimpleObject implements OrderExtensionInterface
{
    /**
     * @inheritdoc
     */
    public function getCustomAttribute()
    {
        return $this->_get(self::CUSTOM_ATTRIBUTE);
    }

    /**
     * @inheritdoc
     */
    public function setCustomAttribute($value)
    {
        return $this->setData(self::CUSTOM_ATTRIBUTE, $value);
    }
}

Here, we have implemented the OrderExtensionInterface in our extension attribute model OrderExtension. We have defined the getter and setter methods for our CUSTOM_ATTRIBUTE extension attribute.

  • Add the extension attribute to the entity model:
<?php
namespace Vendor\Module\Model\Data;

use Magento\Framework\Api\AttributeValueFactory;
use Magento\Framework\Api\ExtensionAttributesInterface;
use Magento\Framework\Api\ExtensionAttributesFactory;
use Vendor\Module\Api\Data\OrderExtensionInterface;

class Order extends \Magento\Sales\Model\Order implements ExtensionAttributesInterface
{
    /**
     * @var ExtensionAttributesFactory
     */
    private $extensionAttributesFactory;

    /**
     * @var AttributeValueFactory
     */
    private $customAttributeFactory;

    /**
     * @var OrderExtensionInterface
     */
    private $extensionAttributes;

    /**
     * @inheritdoc
     */
    public function __construct(
        ExtensionAttributesFactory $extensionAttributesFactory,
        AttributeValueFactory $customAttributeFactory,
        ... // other dependencies
    ) {
        $this->extensionAttributesFactory = $extensionAttributesFactory;
        $this->customAttributeFactory = $customAttributeFactory;
        ... // assign other dependencies
    }

    /**
     * @inheritdoc
     */
    public function setCustomAttributes(array $customAttributes)
    {
        if ($this->extensionAttributes === null) {
            $this->extensionAttributes = $this->_getExtensionAttributes();
        }

        $customAttribute = $this->customAttributeFactory->create();
        $customAttribute->setAttributeCode(OrderExtensionInterface::CUSTOM_ATTRIBUTE);
        $customAttribute->setValue($customAttributes[OrderExtensionInterface::CUSTOM_ATTRIBUTE]);

        $this->extensionAttributes->setAttributes([$customAttribute]);
        return $this;
    }

    /**
     * @inheritdoc
     */
    public function getCustomAttributes()
    {
        if ($this->extensionAttributes === null) {
            $this->extensionAttributes = $this->_getExtensionAttributes();
        }
        return $this->extensionAttributes->getAttributes();
    }

  • Configure the extension attribute in extension_attributes.xml:
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd">
    <extension_attributes for="Magento\Sales\Api\Data\OrderInterface">
        <attribute code="custom_attribute" type="string" />
    </extension_attributes>
</config>

Here, we have defined the custom_attribute extension attribute for the Magento\Sales\Api\Data\OrderInterface entity. The type attribute is set to string, which tells Magento that this is a scalar extension attribute that can hold a string value.

  • Use the extension attribute:

To use the scalar extension attribute in your code, you can simply call the getter method on the entity object. For example:

$order = $this->orderRepository->get(1);
$customAttribute = $order->getExtensionAttributes()->getCustomAttribute();

This will retrieve the value of the custom_attribute extension attribute for the order with ID 1.

  1. Adding a non-scalar extension attribute:

A non-scalar extension attribute is an attribute that can hold a complex value, such as an array or an object. The process for adding a non-scalar extension attribute is similar to that for a scalar attribute, with a few key differences. Here are the steps:

  • Create an interface for the extension attribute:
<?php
namespace Vendor\Module\Api\Data;

interface OrderExtensionInterface extends \Magento\Framework\Api\ExtensionAttributesInterface
{
    const CUSTOM_ATTRIBUTE = 'custom_attribute';

    /**
     * @return \Vendor\Module\Api\Data\CustomObjectInterface|null
     */
    public function getCustomAttribute();

    /**
     * @param \Vendor\Module\Api\Data\CustomObjectInterface $value
     * @return $this
     */
    public function setCustomAttribute(\Vendor\Module\Api\Data\CustomObjectInterface $value);
}

In this example, we have created an interface called OrderExtensionInterface that extends the ExtensionAttributesInterface. We have defined a constant called CUSTOM_ATTRIBUTE that represents the name of our extension attribute. Finally, we have defined two methods, getCustomAttribute() and setCustomAttribute(), which allow us to get and set the value of our non-scalar extension attribute, respectively. Note that in this example, the non-scalar extension attribute is an object that implements the CustomObjectInterface.

  • Implement the interface in the entity model:
<?php
namespace Vendor\Module\Model\Data;

use Vendor\Module\Api\Data\OrderExtensionInterface;

class OrderExtension extends \Magento\Framework\Api\AbstractExtensibleObject implements OrderExtensionInterface
{
    /**
     * @inheritdoc
     */
    public function getCustomAttribute()
    {
        return $this->_get(self::CUSTOM_ATTRIBUTE);
    }

    /**
     * @inheritdoc
     */
    public function setCustomAttribute(\Vendor\Module\Api\Data\CustomObjectInterface $value)
    {
        return $this->setData(self::CUSTOM_ATTRIBUTE, $value);
    }
}

Here, we have implemented the OrderExtensionInterface in our extension attribute model OrderExtension. We have defined the getter and setter methods for our CUSTOM_ATTRIBUTE extension attribute.

  • Add the extension attribute to the entity model:
<?php
namespace Vendor\Module\Model\Data;

use Magento\Framework\Api\AttributeValueFactory;
use Magento\Framework\Api\ExtensionAttributesInterface;
use Magento\Framework\Api\ExtensionAttributesFactory;
use Vendor\Module\Api\Data\OrderExtensionInterface;

class Order extends \Magento\Sales\Model\Order implements ExtensionAttributesInterface
{
    /**
     * @var ExtensionAttributesFactory
     */
    private $extensionAttributesFactory;

   public function __construct(
    ...
    ExtensionAttributesFactory $extensionAttributesFactory,
    AttributeValueFactory $customAttributeValueFactory,
    ...
) {
    ...
    $this->extensionAttributesFactory = $extensionAttributesFactory;
    $this->customAttributeValueFactory = $customAttributeValueFactory;
   }

Here, we are injecting the ExtensionAttributesFactory and AttributeValueFactory classes as dependencies in the constructor. These classes are required to create and manage extension attributes.

  • Override the getExtensionAttributes() and setExtensionAttributes() methods:
phpCopy code/**
 * @inheritdoc
 */
public function getExtensionAttributes()
{
    $extensionAttributes = $this->_getExtensionAttributes();
    if (!$extensionAttributes) {
        $extensionAttributes = $this->extensionAttributesFactory->create(OrderExtensionInterface::class);
        $this->_setExtensionAttributes($extensionAttributes);
    }
    return $extensionAttributes;
}

/**
 * @inheritdoc
 */
public function setExtensionAttributes(ExtensionAttributesInterface $extensionAttributes)
{
    return $this->_setExtensionAttributes($extensionAttributes);
}

Here, we are overriding the getExtensionAttributes() and setExtensionAttributes() methods of the Magento\Sales\Model\Order class to include our non-scalar extension attribute. In the getExtensionAttributes() method, we first check if the extension attributes object has already been created. If not, we create it using the ExtensionAttributesFactory and set it on the entity object using the _setExtensionAttributes() method. The setExtensionAttributes() method simply sets the extension attributes on the entity object.

  • Configure the extension attribute in extension_attributes.xml:
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd">
    <extension_attributes for="Magento\Sales\Api\Data\OrderInterface">
        <attribute code="custom_attribute" type="\Vendor\Module\Api\Data\OrderExtensionInterface" />
    </extension_attributes>
</config>

Here, we have defined the custom_attribute extension attribute for the Magento\Sales\Api\Data\OrderInterface entity. The type attribute is set to \Vendor\Module\Api\Data\OrderExtensionInterface, which tells Magento that this is a non-scalar extension attribute that is defined by our custom interface.

  • Use the extension attribute:

To use the non-scalar extension attribute in your code, you can call the getter method on the entity object and then call the getter or setter methods on the extension attribute object. For example:

$order = $this->orderRepository->get(1);
$customAttribute = $order->getExtensionAttributes()->getCustomAttribute();
$customAttribute->setName('John Doe');

This will retrieve the value of the custom_attribute extension attribute for the order with ID 1 and set the name property of the CustomObjectInterface object to “John Doe”.

Scroll to Top