Les attributs d'extension dans Magento 2
Understanding Magento 2 Extension Attributes
If you've been working with Magento 2 for a while, you've probably encountered situations where you needed to extend core entities like products, commandes, or clients with additional data. That's where extension attributes come into play. They're like little pockets you can sew onto Magento's existing entities to store your custom data without modifying the core database structure.
Think of it this way: Magento vous donne a standard t-shirt (the core entity), and extension attributes let you add custom pockets (your extra data) without altering the original t-shirt design. Pretty neat, right?
Why Use Extension Attributes?
Avant we dive into the how, let's talk about the why:
- Clean integration: No need to modify core tables or create messy contournements
- Future-proof: Your custom data stays safe during Magento mise à jours
- Standardized approche: Follows Magento's bonnes pratiques for extending fonctionality
- API-friendly: Automatically available through REST/SOAP APIs
How Extension Attributes Work
The extension attribute system in Magento 2 is built around three main composants:
- Declaration: Telling Magento about your new attribute
- Data handling: Where and comment store your attribute's valeur
- Retrieval: How to access your attribute when needed
Step-by-Step Implementation
Parcourons ensemble a practical exemple of adding an extension attribute to products that stores a "handling_time" valeur (how many days it takes to prepare the product for shipping).
1. Create the extension_attributes.xml File
Premièrement, we need to declare our attribute. Create this fichier in your module:
<!-- app/code/Vendor/Module/etc/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\Catalog\Api\Data\ProductInterface">
<attribute code="handling_time" type="int">
<join reference_table="vendor_module_product_handling_time" reference_field="product_id" join_on_field="entity_id">
<field column="handling_days">handling_time</field>
</join>
</attribute>
</extension_attributes>
</config>
This tells Magento we're adding a "handling_time" attribute (integer type) to products, and it sera stored in a custom table.
2. Create the Database Table
Now let's create the table to store our data. Create an InstallSchema.php fichier:
<?php
namespace Vendor\Module\Setup;
use Magento\Framework\Setup\InstallSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
class InstallSchema implements InstallSchemaInterface
{
public function install(SchemaSetupInterface $setup, ModuleContextInterface $context)
{
$installer = $setup;
$installer->startSetup();
$table = $installer->getConnection()->newTable(
$installer->getTable('vendor_module_product_handling_time')
)->addColumn(
'id',
\Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
null,
['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true],
'ID'
)->addColumn(
'product_id',
\Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
null,
['unsigned' => true, 'nullable' => false],
'Product ID'
)->addColumn(
'handling_days',
\Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
null,
['nullable' => false, 'default' => '0'],
'Handling Days'
)->addIndex(
$installer->getIdxName('vendor_module_product_handling_time', ['product_id']),
['product_id']
)->addForeignKey(
$installer->getFkName(
'vendor_module_product_handling_time',
'product_id',
'catalog_product_entity',
'entity_id'
),
'product_id',
$installer->getTable('catalog_product_entity'),
'entity_id',
\Magento\Framework\DB\Ddl\Table::ACTION_CASCADE
)->setComment(
'Product Handling Time Table'
);
$installer->getConnection()->createTable($table);
$installer->endSetup();
}
}
3. Create a Plugin to Handle Data Loading
Nous devons ensure our handling_time data gets loaded with the product. Create a plugin for the product repository:
<?php
namespace Vendor\Module\Plugin;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;
class ProductLoadPlugin
{
public function afterGet(
ProductRepositoryInterface $subject,
ProductInterface $product
) {
$extensionAttributes = $product->getExtensionAttributes();
// Get handling time from our custom table
$handlingTime = $this->getHandlingTimeForProduct($product->getId());
$extensionAttributes->setHandlingTime($handlingTime);
$product->setExtensionAttributes($extensionAttributes);
return $product;
}
private function getHandlingTimeForProduct($productId)
{
// Implement your logic to fetch handling time from custom table
// This is simplified for example purposes
return 3; // Default handling time
}
}
4. Register the Plugin
Add this to your module's di.xml:
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Catalog\Api\ProductRepositoryInterface">
<plugin name="vendor_module_product_load" type="Vendor\Module\Plugin\ProductLoadPlugin" />
</type>
</config>
5. Saving the Extension Attribute
To save our handling_time when products are saved, create another plugin:
<?php
namespace Vendor\Module\Plugin;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;
class ProductSavePlugin
{
public function beforeSave(
ProductRepositoryInterface $subject,
ProductInterface $product,
$saveOptions = false
) {
$extensionAttributes = $product->getExtensionAttributes();
if ($extensionAttributes && $extensionAttributes->getHandlingTime() !== null) {
$this->saveHandlingTimeForProduct(
$product->getId(),
$extensionAttributes->getHandlingTime()
);
}
return [$product, $saveOptions];
}
private function saveHandlingTimeForProduct($productId, $handlingTime)
{
// Implement your logic to save handling time to custom table
}
}
And register it in di.xml:
<type name="Magento\Catalog\Api\ProductRepositoryInterface">
<plugin name="vendor_module_product_save" type="Vendor\Module\Plugin\ProductSavePlugin" />
</type>
Accessing Extension Attributes
Now that we've set everything up, here's comment access our handling_time attribute:
$product = $this->productRepository->getById($productId);
$handlingTime = $product->getExtensionAttributes()->getHandlingTime();
Or through the API:
GET /rest/V1/products/{sku}
The response will include:
{
"extension_attributes": {
"handling_time": 3
}
}
Advanced Usage
Using Extension Attributes with Collections
To efficiently load extension attributes when working with product collections, you'll need to modify the collection:
$collection = $this->productCollectionFactory->create();
$collection->addAttributeToSelect('*');
// Join our custom table
$collection->joinTable(
['handling_time' => 'vendor_module_product_handling_time'],
'product_id = entity_id',
['handling_time' => 'handling_days'],
null,
'left'
);
foreach ($collection as $product) {
$extensionAttributes = $product->getExtensionAttributes();
$extensionAttributes->setHandlingTime($product->getData('handling_time'));
$product->setExtensionAttributes($extensionAttributes);
}
Custom Data Types
Extension attributes aren't limited to simple types. Vous pouvez use complex objets too. Premièrement, define your data interface:
<?php
namespace Vendor\Module\Api\Data;
interface HandlingTimeInterface
{
public function getDays();
public function setDays($days);
public function getNotes();
public function setNotes($notes);
}
Then implement it:
<?php
namespace Vendor\Module\Model\Data;
use Vendor\Module\Api\Data\HandlingTimeInterface;
class HandlingTime implements HandlingTimeInterface
{
private $days;
private $notes;
public function getDays()
{
return $this->days;
}
public function setDays($days)
{
$this->days = $days;
return $this;
}
public function getNotes()
{
return $this->notes;
}
public function setNotes($notes)
{
$this->notes = $notes;
return $this;
}
}
Update your extension_attributes.xml:
<attribute code="handling_info" type="Vendor\Module\Api\Data\HandlingTimeInterface">
Common Pitfalls and Bonnes pratiques
Après implementing many extension attributes, here are some lessons learned:
- Performance matters: Always join tables properly when working with collections to avoid the N+1 query problem
- Null handling: Always check if extensionAttributes exists before trying to access it
- API exposure: Remember that extension attributes are automatically exposed through APIs - don't include sensitive data
- Validation: Implement proper validation in your save plugins
- Indexers: If your attribute affects frontend display, consider creating a custom indexeur
When Not to Use Extension Attributes
Tandis que extension attributes are powerful, they're not always the right solution:
- For simple yes/no flags, consider attribut produits instead
- Quand vous need the data to be rechercheable/filtreable in the grille d'administration
- For data that needs complex SQL queries or aggregations
Conclusion
Magento 2 extension attributes provide a clean, maintainable way to extend core entities with custom data. By following the patterns we've covered, you can add powerful fonctionality to your store while maintaining mise à jour compatibility and following Magento bonnes pratiques.
Remember that proper implémentation requires attention to both the declaration (extension_attributes.xml) and the data handling (plugins/repositories). When done correctly, extension attributes seamlessly integrate with Magento's existing systems, including APIs and contrat de services.
Now that you understand how extension attributes work, what custom data will you add to your Magento store?