Como sobrescrever arquivos no Magento 2 através de preferences
Tags: php, magento2, programming, braziliandevs
Published: 2021-10-25 12:18:37

# Contextualizando

### Magento 2 e a injeção de dependência

Para entender melhor a injeção de dependência é importante o conhecimento do 5º princípio do **SOLID**, a Inversão de Dependência (*Dependency Inversion Principle*). 

É chamada de dependência cada instância de objeto dentro de uma determinada classe, porque a classe fica dependente da instância do objeto para ser executada. Para a injeção desta instância, é recomendado, que seja realizado através do DIP (Inversão de dependência).

A Injeção de dependência é um padrão de projeto (*design pattern*) que permite uma classe mantenha um baixo nível de acoplamento entre diferentes módulos do sistema, ou seja, permite que uma classe não seja responsável por criar os objetos dos quais depende.

### Object Manager

O *Object Manager* é uma classe de serviço do Magento que instancia objetos no início do processo de *bootstrap*, gerando e injetando as classes declaradas no arquivo `di.xml` como dependências nos construtores das classes instanciadas.

Como o *Object Manager* fornece seu serviço indiretamente, a classe não deve depender do próprio *Object Manager*. As únicas exceções são *factories* personalizadas com lógica complexa e integrações de testes que necessitam das configurações de ambiente.

O Magento utiliza sua ferramenta de compilação de código para coletar todas as informações das dependências das classes e as armazena em arquivos. Durante o processo de criação das classes, o *Object Manager* utiliza essas informações de criação para criar objetos concretos na aplicação.

Classes de serviço que não existem no código base, como *proxies*, *factories* e *interceptors* que são declarados no código ou nas configurações, são gerados com a ajuda do compilador.

### O que são as preferências?

As preferências (*preferences*) *são* utilizadas para indicar a implementação padrão das interfaces através do *Object Manager*, caso a interface injetada no construtor de uma classe não tenha sido mapeada com um *preference,* ao instanciar a classe será exibido um erro. Para a que o *Object Manager* faça o mapeamento da interface para a classe, deve-se utilizar o nó `<preference>` no arquivo `di.xml`, indicando no atributo `for` qual a interface e no atributo `type` qual a classe que deverá ser sobrescrita e implementar os métodos determinados pela interface.

A *preference* não precisa ser utilizada apenas com interfaces, é possível sobrescrever uma classe de outro módulo, definindo a nova classe globalmente. Para que o *Object Manager* faça o mapeamento da classe que deve ser sobrescrita, deve-se utilizar o nó `<preference>` no arquivo `di.xml`, indicando no atributo `for` qual a classe que será sobrescrita e no atributo `type` qual a classe que deverá ser sobrescrever, e nesta sobrepor o método com a mesma assinatura (desde que ele seja do publico ou protegido).

---

# Código para sobrescrever arquivos

### di.xml

O *Object Manager* usa a implementação através da abstração mapeando quando a assinatura do construtor de uma classe solicita um objeto por sua interface. O *Object Manager* utiliza esse mapeamento para determinar qual é a implementação padrão para a classe de um escopo específico. Estes tipo de funcionalidade deve ser implementada dentro do arquivo `di.xml`, seguindo a estruturas de pastas `\{Vendor}\{Module}\etc\{area}\di.xml`.

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

    <!-- Override a Magento Class -->
    <preference for="{Vendor}\{Module}\{Directory}\{ClassToReplace}" type="{Vendor}\{Module}\{Directory}\{ClassReplaced}" />
</config>
```

### Interface

O código a seguir se refere a interface de uma entidade que deverá receber uma sobrescrição.

```php
<?php

namespace {Vendor}\{Module}\Api\Data;

interface {EntityName}Interface
{
    public const ATTR_NAME = '{attribute_name}';

    public function getMethod(): {type};
    
    public function setMethod(): self;
}
```

### Model da Interface

O código a seguir se refere a implementação dos métodos de uma interface.

```php
<?php

namespace {Vendor}\{Module}\Model;

use {Vendor}\{Module}\Api\Data\{EntityName}Interface;
use {Vendor}\{Module}\Model\ResourceModel\{ResourceModelName};

class {EntityName} implements {EntityName}Interface
{
    public function getMethod(): {type}
    {
        return $this->getData(self::ATTR_NAME);
    }
    
    public function setMethod(string $attrName): self
    {
        return $this->setData(self::ATTR_NAME, $attrName);
    }
}
```

---

### Classe a substituir

O código a seguir se refere a sobrescrição do método de uma classe, podendo ser uma classe de qualquer tipo (*Block*, *Controller*, *Data Provider*, *Model*, *Ui Component*, *Service*, etc).

```php
<?php

namespace {Vendor}\{Module}\{Directory};

class {ClassToReplace}
{
    public function {methodName}(): {type}
    {
        // Old code here
    }
}
```

### Classe substituída

O código a seguir se refere a extensão do(s) método(s) de uma classe.

```php
<?php

namespace {Vendor}\{Module}\{Directory};

use {Vendor}\{Module}\{Directory}\{ClassToReplace};

class {ClassReplaced} extends {ClassToReplace}
{
    public function {methodName}(): {type}
    {
        // New code here
    }
}
```

---

# Finalizando

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

## Habilitando as alterações

Comando para gerar a configuração das injeções de dependência e todas as classes ausentes que precisam ser geradas (proxys, interceptors, etc).

```bash
php bin/magento setup:di:compile
```

## Diretórios e Arquivos

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

```
- app/
  - code/
    - {Vendor}/
        - {Module}/
          - Api/
            - Data/
              - {EntityName}Interface.php
          - etc/
            - di.xml
            - module.xml
          - {Directory}/
            - {ClassReplaced}.php
          - Model/
            - {EntityName}.php
          - registration.php
          - composer.json
```