Quick overview of the entity attribute system
Since the early days, Magento has offered an extensible entity attribute system based on the Entity-Attribute-Value (EAV) model.
Through this system, it’s straightforward to extend a Magento entity, with some limits:
- only EAV entities can be extended with attributes; not all entities in a default Magento installation are EAV entities, just products, categories, customers, and customer addresses.
- attributes can only contain scalar values, that is,
The extension attributes system, introduced in Magento 2, overcomes the above limits, making it easy to:
- extend non-EAV entities to a condition: the entity should implement the
- use objects to have more complex types.
Unluckily, some Magento core entities don’t implement the
\Magento\Framework\Api\ExtensibleDataInterface thus they are not extensible with native extension attributes. Anyway, let’s focus on the half-full glass to explore some advantages of the extension attributes system.
What are custom attributes?
With the introduction of the extension attributes system, some new terminology comes in.
EAV attributes belong to one of the following sets:
- system attributes – created by any default Magento installation;
- custom attributes – created in the Admin Panel or through data patches.
So, simply put, a custom attribute is an EAV attribute created by someone else.
What are extension attributes?
We have already given a definition but let’s refresh it: an extension attribute is an attribute that extends a non-EAV entity.
But obviously, there is more.
First, the good news: an extension attribute allows us to overcome the limitations of scalar values. With an extension attribute, we can extend both EAV and non-EAV with complex objects.
For example, the
gift_message extension attribute added to the order (
Magento\Sales\Api\Data\OrderInterface) is of the following type:
* Constants for keys of data array. Identical to the name of the getter in snake case
const GIFT_MESSAGE_ID = 'gift_message_id';
const CUSTOMER_ID = 'customer_id';
const SENDER = 'sender';
const RECIPIENT = 'recipient';
const MESSAGE = 'message';
The price we pay for such flexibility is that extension attribute values persistency needs to be populated programmatically, whereas custom attribute values are loaded and saved automatically.
Refer to the official documentation for more details on how to add extension attributes to entity.
Let’s note that
ExtensibleDataInterface doesn’t declare the methods to access extension attributes. Here is its declaration:
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
* Interface for entities which can be extended with extension attributes.
* Key for extension attributes object
const EXTENSION_ATTRIBUTES_KEY = 'extension_attributes';
By convention, the methods are named
getExtensionAttributes() but we can’t give for granted that a class implementing
ExtensibleDataInterface provides those methods, so my advice is to double check it.
It’s worth mentioning that to prevent performance degradation while fetching extension attributes in collections, Magento provides a native join functionality (documented here), but this feature is limited to scalar extension attributes.
To find an example, we can look at the declaration of the
is_subscribed attribute added to a customer in the
<attribute code="is_subscribed" type="boolean" >
💡 Let’s pay attention: for the join to work, it’s not enough to declare the above join; we also have to pass the collection to the
\Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface::process() method to have it populated with correct data. We can see an example in the
getList() method of
public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria)
/** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $collection */
$collection = $this->collectionFactory->create();
Another relevant feature is that we can restrict access to the values of an entity’s extension attribute by declaring an ACL resource, as shown in the official documentation.
Where to store extension attributes?
Usually, extension attributes are non-EAV attributes, meaning that we should decide where to persist their values.
If the extension attribute is a complex object, the default choice would be to use a separate custom table.
But if the extension attribute is a scalar, extending core tables with a non-ambiguous custom column is acceptable. This way, persisting values will require less code and will be more performant because it avoids additional join conditions.
There isn’t a golden rule, though; we need to properly analyze every case before making the right decision.
Custom attributes are nothing new in the Magento world: just a new name given to EAV-entity attributes that Magento installation does not create.
Extension attributes, instead, are a new way to extend non-EAV entities.
With extension attributes also comes the possibility to define complex attributes, overcoming the limit of scalar types.
All this comes at a bit of cost: we have to carefully handle persistence to ensure that data is retrieved and saved the way we expect.