Magento 2 Adding Extension Attributes To Entity

It is impossible for third-party developers to change the API data interfaces which are defined in the Magento core code. Nevertheless, most of these entities come with a feature called extension attributes. In this article, we will show you how to add extension attributes to the entity in Magento 2. Let’s see how it is done!

Step 1: Retrieve A Product/ List Of Products From The Magento API

To recall a product or a list of products from the Magento API, you will need to make an API request to the suitable service (the Product Repository in this case). The response to these requests will returns objects with the following structure:

Product Response:

<product>
    <id>1</id>
    <sku>some-sku</sku>
    <custom_attributes><!-- Custom Attributes Data --></custom_attributes>
    <extension_attributes><!-- Here should we add extension attributes data --></extension_attributes>
</product>

Product List Response:

<products>
    <item>
        <id>1</id>
        <sku>some-sku</sku>
        <custom_attributes><!-- Custom Attributes Data --></custom_attributes>
        <extension_attributes><!-- Here should we add extension attributes data --></extension_attributes>
    </item>
    <item>
        <id>2</id>
        <sku>some-sku-2</sku>
        <custom_attributes><!-- Custom Attributes Data --></custom_attributes>
        <extension_attributes><!-- Here should we add extension attributes data --></extension_attributes>
    </item>
</products>

Step 2: Add Plugin To Product Repository

Using an after plugin on Product Repository is necessary to add an extension attributes. The plugin need to follow the methods including save, get, getList. We can add scalar which is a simple attribute, and non-scalar which can be shown by Data Object.

public function afterGet
(
    \Magento\Catalog\Api\ProductRepositoryInterface $subject,
    \Magento\Catalog\Api\Data\ProductInterface $entity
) {
    $customData = $this->customDataRepository->get($entity->getId());

    $extensionAttributes = $entity->getExtensionAttributes(); /** get current extension attributes from entity **/
    $extensionAttributes->setCustomData($customData);
    $entity->setExtensionAttributes($extensionAttributes);

    return $entity;
}

AfterGet plugin:

Here is the simplest way to add extensions without causing a conflict:

  • Firstly, we get the entity’s extension attributes, in case they are already set.
  • Secondly, we add our extension attribute.
  • Finally, we set the extension attribute on the entity with ours included.
AfterGetList plugin:

You can do similar to afterGetList.

AfterSave plugin:

The afterSave plugin should manipulate the entity data before returning it:

public function afterSave
(
    \Magento\Catalog\Api\ProductRepositoryInterface $subject,
    \Magento\Catalog\Api\Data\ProductInterface $entity
) {
    $extensionAttributes = $entity->getExtensionAttributes(); /** get current extension attributes from entity **/
    $customData = $extensionAttributes->getCustomData();
    $this->customDataRepository->save($customData);

    return $entity;
}

However, in this case some entities don’t have the implementation to fetch extension attribute, you will always retrieve null value. Every time when you fetch extension attributes, you will need to determine whether they are null or not. If they are null, we need to create them. To prevent such code duplication, we need to create afterGet plugin for our entity with extension attributes.

Let’s consider that the product entity doesn’t have any implementation of extension attributes, so you can view the our plugin, as following:

use Magento\Catalog\Api\Data\ProductExtensionInterface;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Api\Data\ProductExtensionFactory;

class ProductAttributesLoad
{
    /**
     * @var ProductExtensionFactory
     */
    private $extensionFactory;

    /**
     * @param ProductExtensionFactory $extensionFactory
     */
    public function __construct(ProductExtensionFactory $extensionFactory)
    {
        $this->extensionFactory = $extensionFactory;
    }

    /**
     * Loads product entity extension attributes
     *
     * @param ProductInterface $entity
     * @param ProductExtensionInterface|null $extension
     * @return ProductExtensionInterface
     */
    public function afterGetExtensionAttributes(
        ProductInterface $entity,
        ProductExtensionInterface $extension = null
    ) {
        if ($extension === null) {
            $extension = $this->extensionFactory->create();
        }

        return $extension;
    }
}

Now we need attach our plugin to ProductInterface:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Catalog\Api\Data\ProductInterface">
        <plugin name="ProductExtensionAttributeOperations" type="Magento\Catalog\Plugin\ProductAttributesLoad"/>
    </type>
</config>

Step 3: Configure Extension Attributes

For Scalar Attributes:

You should use the below configuration:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd">
    <extension_attributes for="Magento\Catalog\Api\Data\ProductInterface">
        <attribute code="first_custom_attribute" type="Magemeta\CustomData\Api\Data\CustomDataInterface" />
        <attribute code="second_custom_attribute" type="Magemeta\CustomData\Api\Data\CustomDataInterface" />
    </extension_attributes>
</config>

Then, you will get the following result:

<product>
    <id>1</id>
    <sku>some-sku</sku>
    <custom_attributes><!-- Custom Attributes Data --></custom_attributes>
    <extension_attributes>
        <first_custom_attribute>1</first_custom_attribute>
        <second_custom_attribute>2</second_custom_attribute>
    </extension_attributes>
</product>
For Non-scalar Attributes

You should use below configuration:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd">
    <extension_attributes for="Magento\Catalog\Api\Data\ProductInterface">
        <attribute code="custom_data" type="Magemeta\CustomData\Api\Data\CustomDataInterface[]" />
    </extension_attributes>
</config>

Then, you will get the following result:

<product>
    <id>1</id>
    <sku>some-sku</sku>
    <custom_attributes><!-- Custom Attributes Data --></custom_attributes>
    <extension_attributes>
        <our_custom_data>
            <first_custom_attribute>1</first_custom_attribute>
            <second_custom_attribute>2</second_custom_attribute>
        </our_custom_data>
    </extension_attributes>
</product>

Hope this helps you guys!

Leave a Reply