DEV Community

Lucas Teixeira dos Santos Santana
Lucas Teixeira dos Santos Santana

Posted on • Edited on

Como criar atributos de extensão no Magento 2

Contextualizando

O que é um atributo de extensão?

Os atributos no Magento 2 são propriedades das entidades que auxiliam na variedade de opções. Os módulos personalizados não podem alterar as interfaces das entidades nativas, entretanto, a maioria das interfaces das entidades possuem um funcionalidade chamada atributos de extensão. Para saber se a interface da entidade possui atributos de extensão, basta observar se a mesma estende da interface \Magento\Framework\Api\ExtensibleDataInterface e possui os métodos getExtensionAttributes() e setExtensionAttributes().


Código para criar um atributo

Criando o atributo de extensão

extension_attributes.xml

Para criar os atributos de extensão é necessário criar um arquivo chamado extension_attributes.xml que deve seguir a estrutura de pasta \{Vendor}\{Module}\etc\extension_attributes.xml

<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd"> <extension_attributes for="{Vendor}\{Module}\Api\Data\{EntityName}Interface"> <attribute code="{attr_name_object}" type="{Vendor}\{Module}\{Directory}\{Class}" /> <attribute code="{attr_name_string}" type="string" /> <attribute code="{attr_name_boolean}" type="boolean" /> <attribute code="{attr_name_integer}" type="int" /> <attribute code="{attr_name_float}" type="float" /> <attribute code="{attr_name_array}" type="{type}[]" /> <attribute code="{attr_name_array}" type="{Vendor}\{Module}\Api\Data\{EntityName}Interface[]"> <resources> <resource ref="{Vendor}_{Module}::{acl_path}" /> </resources> <join reference_table="{table_name}" reference_field="{column_name}" join_on_field="{interface_field}"> <field>{field_name}</field> </join> </attribute> </extension_attributes> </config> 
Enter fullscreen mode Exit fullscreen mode

O nó <extension_attributes> só possui um atributo, o for, que indicará qual a entidade que receberá os novos atributos de extensão e que será implementada a interface \Magento\Framework\Api\ExtensibleDataInterface e possuir os métodos getExtensionAttributes() e setExtensionAttributes().

O nó <attribute> é filho do nó <extension_attributes> e é obrigatório, este possui o atributo code, que será o nome do atributo (deverá ser escrito em snake case) e o atributo type, que indicará o tipo do atributo.

O nó <resources> é filho do nó <attribute> é opcional e não possui atributos, apenas pode ter o nó filho <resource>, já este possui o atributo ref, que referencia a seção a um recurso ACL para fornecer permissões das configurações.

O nó <join> é filho do nó <attribute> é opcional e possui o atributo reference_table, que faz uma operação de join com a tabela referenciada neste atributo, com o valor da coluna referenciada no atributo reference_field e join_on_field é a coluna da tabela associada a interface.

O nó <field> é filho do nó <join> e é obrigatório quando este for inserido. Este nó não possui atributos e nem outros nós filhos, apenas valor, que especifica a propriedade da tabela referenciada que irá unir a interface definida.

db_schema.xml

É necessário adicionar uma coluna na tabela desejada para o campo do atributo de extensão criado no arquivo extension_attributes.xml.
Utilizarei o arquivo de db_schema.xml para adicionar a coluna na tabela, mas pode ser feito como UpgradeSchema ou SchemaPatch.

<?xml version="1.0" ?> <schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd"> ... <table name="{table_name)"> <column xsi:type="{column_type}" name="{column_name)" comment="{Column Comment}"/> </table> </schema> 
Enter fullscreen mode Exit fullscreen mode

Para saber mais sobre como criar e/ou atualizar campos na tabela via db_schema.xml basta acessar o post "Como criar tabelas personalizadas no banco de dados com db_schema.xml no Magento 2".
Para saber mais sobre como criar e/ou atualizar campos na tabela via SchemaPatch basta acessar o post "Como criar/atualizar tabelas e dados com DataPatch e SchemaPatch no Magento 2".
Para saber mais sobre como criar e/ou atualizar campos na tabela na forma antiga que o Magento utiliza, UpgradeSchema, basta acessar o post "Como criar/atualizar tabelas com InstallSchema e UpgradeShema no Magento 2".

Acessando os atributos de extensão

Assim que criar o arquivo extension_attributes.xml já fica disponível para acessar e definir valores dos atributos via métodos mágicos, apenas adicionando o prefixo get e set nos nomes definidos dos atributos.

$extAttributes = $entityName->getExtensionAttributes(); $extAttributes->getAttrNameObject(); $extAttributes->getAttrNameString(); $extAttributes->getAttrNameBoolean(); $extAttributes->getAttrNameInteger(); $extAttributes->getAttrNameFloat(); $extAttributes->getAttrNameArray(); $extAttributes->setAttrNameObject($objectValue); $extAttributes->setAttrNameString($stringValue); $extAttributes->setAttrNameBoolean($booleanValue); $extAttributes->setAttrNameInteger($integerValue); $extAttributes->setAttrNameFloat($floatValue); $extAttributes->setAttrNameArray($arrayValue); 
Enter fullscreen mode Exit fullscreen mode

Atributo de extensão na entidade

Interface

No Magento, primeiro criamos a interface do repositório no caminho \{Vendor}\{Module}\Api\Data. Caso a interface que queira adicionar os atributos de extensão não pertença ao código fonte do Magento 2, basta herdar a interface \Magento\Framework\Api\ExtensibleDataInterface.
Para saber mais sobre como sobreescrever arquivos através no post "Como sobrescrever arquivos no Magento 2 através de preferences".

<?php namespace {Vendor}\{Module}\Api\Data; use Magento\Framework\Api\ExtensibleDataInterface; interface {EntityName}Interface extends ExtensibleDataInterface { public const {ATTR_NAME} = '{attribute_name}'; public function get{MethodName}(): {type}; public function set{MethodName}(): self; public function getExtensionAttributes(): \{Vendor}\{Module}\Api\Data\{EntityName}ExtensionInterface; public function setExtensionAttributes(\{Vendor}\{Module}\Api\Data\{EntityName}ExtensionInterface $extensionAttributes): self; } 
Enter fullscreen mode Exit fullscreen mode

di.xml

Após a criação da interface do repositório é necessário a sobrescrição através do arquivo di.xml, para que o arquivo fique devidamente implementado e possa descrever em como os métodos serão executados.

<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> ... <preference for="{Vendor}\{Module}\Api\Data\{EntityName}Interface" type="{Vendor}\{Module}\Model\Data\{EntityName}"/> ... </config> 
Enter fullscreen mode Exit fullscreen mode

Model

Criado a interface do repositório e a sobrescrição da interface através do arquivo di.xml, falta apenas implementar os métodos da interface na classe.

<?php namespace {Vendor}\{Module}\Model\Data; use Magento\Framework\Model\AbstractExtensibleModel; use {Vendor}\{Module}\Api\Data\{EntityName}Interface; class {EntityName} extends AbstractExtensibleModel implements {EntityName}Interface { public function get{MethodName}(): {type} { return $this->_get({EntityName}Interface::{ATTR_NAME}); } public function set{MethodName}(string $attrName): {EntityName}Interface { return $this->setData({EntityName}Interface::ATTR_NAME, $attrName); } public function getExtensionAttributes(): \{Vendor}\{Module}\Api\Data\{EntityName}ExtensionInterface { return $this->_getExtensionAttributes(); } public function setExtensionAttributes(\{Vendor}\{Module}\Api\Data\{EntityName}ExtensionInterface $extensionAttributes) { return $this->_setExtensionAttributes($extensionAttributes); } } 
Enter fullscreen mode Exit fullscreen mode

Plugin para salvar/recuperar os dados do atributo de extensão

di.xml

Para saber mais sobre plugin basta acessar o post "Como criar plugins no Magento 2".

<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> ... <type name="Magento\{Module}\Model\{Directory}\{EntityName}"> <plugin name="{plugin_name}" type="{Vendor}\{Module}\Plugin\Model\{PluginEntityName}" disabled="false" sortOrder="10" /> </type> <type name="Magento\{Module}\Model\{Directory}\{EntityName}Repository"> <plugin name="{plugin_name}" type="{Vendor}\{Module}\Plugin\{Directory}\{PluginRepositoryName}" disabled="false" sortOrder="20" /> </type> ... </config> 
Enter fullscreen mode Exit fullscreen mode

Plugins

Será necessário criar o método afterLoad() para adicionar os atributos de extensão no retorno do objeto quando forem recuperado do banco via a classe da entidade.
Será necessário criar o método beforeSave() para adicionar valores no atributo de extensão antes de salvar o objeto no banco de dados através da entidade.

<?php namespace {Vendor}\{Module}\Plugin\{Directory}; use Magento\{Module}\Api\Data\{EntityName}ExtensionFactory; use Magento\{Module}\Api\Data\{EntityName}Interface as Magento{EntityName}Interface; use {Vendor}\{Module}\Api\Data\{EntityName}Interface; class {PluginEntityName} { public function __construct( private {EntityName}ExtensionFactory $extensionAttributeFactory ) { } public function afterLoad(Magento{EntityName}Interface $subject): Magento{EntityName}Interface { $extensionAttributes = $subject->getExtensionAttributes() ?: $this->extensionAttributeFactory->create(); $extensionAttributes->set{MethodName}($subject->getData({EntityName}Interface::ATTR_NAME)); $subject->setExtensionAttributes($extensionAttributes); return $subject; } public function beforeSave(Magento{EntityName}Interface $subject): array { $extensionAttributes = $subject->getExtensionAttributes() ?: $this->extensionAttributeFactory->create(); if ($extensionAttributes !== null && $extensionAttributes->set{MethodName}() !== null) { $subject->set{MethodName}($extensionAttributes->get{MethodName}()); } return [$subject]; } } 
Enter fullscreen mode Exit fullscreen mode

Será necessário criar os métodos afterGetById() e afterGetList() para adicionar os atributos de extensão no retorno do(s) objeto(s) quando forem recuperado do banco via o repositório.
Será necessário criar o método beforeSave() para adicionar valores no atributo de extensão antes de salvar o objeto no banco de dados através do repositório.

<?php namespace {Vendor}\{Module}\Plugin\{Directory}; use Magento\{Module}\Api\Data\{EntityName}ExtensionFactory; use Magento\{Module}\Api\{EntityName}RepositoryInterface; use {Vendor}\{Module}\Api\Data\{EntityName}Interface; use Magento\{Module}\Api\Data\{EntityName}Interface as Magento{EntityName}Interface; use Magento\{Module}\Api\Data\{EntityName}SearchResultsInterface; class {PluginRepositoryName} { public function __construct( private {EntityName}ExtensionFactory $extensionAttributeFactory ) { } public function afterGetById( {EntityName}RepositoryInterface $subject, Magento{EntityName}Interface $entity ): Magento{EntityName}Interface { $extensionAttributes = $entity->getExtensionAttributes() ?: $this->extensionAttributeFactory->create(); $extensionAttributes->set{MethodName}($entity->getData({EntityName}Interface::ATTR_NAME)); $entity->setExtensionAttributes($extensionAttributes); return $entity; } public function afterGetList( {EntityName}RepositoryInterface $subject, {EntityName}SearchResultsInterface $resultCart ) { /** @var Magento{EntityName}Interface $entity */ foreach ($resultCart->getItems() as $entity) { $this->afterGetById($subject, $entity); } return $resultCart; } public function beforeSave( {EntityName}RepositoryInterface $subject, Magento{EntityName}Interface $entity ): array { $extensionAttributes = $entity->getExtensionAttributes() ?: $this->extensionAttributeFactory->create(); if ($extensionAttributes !== null && $extensionAttributes->set{MethodName}() !== null) { $entity->set{MethodName}($extensionAttributes->get{MethodName}()); } return [$entity]; } } 
Enter fullscreen mode Exit fullscreen mode

Finalizando

Valores entre chaves ({test}) devem ser alterados na implementação do código.

Habilitando as alterações

Apague os arquivos que são gerados na compilação do Magento e execute o comando PHP para gerar a configuração das injeções de dependência e todas as classes ausentes que precisam ser geradas (proxys, interceptors, etc) e para limpar todos os caches de armazenamento em cache do processos.

rm -rf var/generation/ rm -rf generated/ php bin/magento setup:di:compile php bin/magento cache:clean php bin/magento flush 
Enter fullscreen mode Exit fullscreen mode

Diretórios e Arquivos

Segue a a lista de diretórios e arquivos que devem ser criados.

- app/ - code/ - {Vendor}/ - {Module}/ - etc/ - module.xml - extension_attributes.xml - registration.php - composer.json 
Enter fullscreen mode Exit fullscreen mode

Top comments (0)