Como criar plugins no Magento 2
Tags: php, magento2, tutorial, braziliandevs
Published: 2021-11-05 12:19:59

## Contextualizando

Um *plugin*, ou *interceptor*, é uma classe que modifica o comportamento de funções públicas de outras classes, interceptando uma chamada de função. Isso permite estender ou substituir o comportamento de métodos públicos originais para qualquer classe ou interface.

Essa abordagem de interceptação reduz conflitos entre extensões que alteram o comportamento da mesma classe ou método. A implementação da classe do tipo plugin altera o comportamento de uma função da classe especificada no `di.xml`, mas não altera a classe em si. O Magento chama os *interceptors* sequencialmente de acordo com a ordem de classificação, então não podem ter conflitos uns com os outros.

---

## Código para criar plugin

### di.xml

O primeiro argumento para os métodos *before*, *after* e *around* é o objeto que fornece o acesso a todos os métodos públicos da classe do método observado. Estes tipo de funcionalidade deve ser implementada dentro do arquivo `di.xml`, seguindo a estruturas de pastas `\{Vendor}\{Module}\etc\{area}\di.xml`. Já os arquivos com a lógica que serão executados devem seguir a estrutura de pastas `\{Vendor}\{Module}\Plugin\{PluginName}` e não devem estender nenhuma classe, apenas conter o nome do método que será adicionado a funcionalidade com o sufixo necessário.

Parâmetros obrigatórios para a declaração de um plugin:

- **Type name**: uma classe ou interface que o plugin irá observar
- **Plugin name**: um nome que identifica o plugin. Também utilizado para mesclar configurações do plugin.
- **Plugin type**: nome da classe do plugin ou nome do tipo virtual.

Parâmetros não obrigatórios para a declaração de um plugin:

- **Plugin sort order**: plugins que chamam o mesmo método seguem a ordem crescente desse atributo
- **Plugin disabled**: para desabilitar um plugin, deve definir esse atributo como *true*. O valor padrão é *false*

```xml
<?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="{Vendor}\{Module}\{Directory}\{ClassName}">
        <plugin name="{plugin_name}" type="{Vendor}\{Module}\Plugin\{PluginName}" disabled="false" sortOrder="10" />
    </type>
</config>
```

### Before

O Magento executa todos os métodos *before* antes da chamada para o método observado.

É possível utilizar métodos *before* para alterar argumentos de um método observado, retornando um argumento modificado. Se existirem muitos argumentos, o método deve retornar um *array* com todos os argumentos. Se o método não alterar o argumento do método observado, ele deverá retornar *null*.

```php
<?php

namespace {Vendor}\{Module}\Plugin;

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

class {PluginName}
{
    public function before{MethodName}({ClassName} $subject): void
    {
        // Code here
    }
}
```

### After

O Magento executa todos os métodos *after* após a conclusão da execução do método observado. Esse tipo de método é obrigatório ter um retorno. É possível utilizar métodos *after* para alterar o resultado de um método observado. modificando o resultado original e retornando o valor alterado no final do método.

Os métodos *after* possuem acesso a todos os argumentos do método observado. Quando o método observado termina sua execução, o Magento passa o resultado e os argumentos para o método *after* seguinte (caso exista mais de um método *after* para o mesmo método observado). Se o método observado não possui retorno (*void*), então o método *after* deverá retornar *null*.

Os métodos *after* não precisam declarar todos os argumentos do método observado, exceto o argumento que o método do plugin irá utilizar e todos os seus anteriores (os posteriores não são necessários).

```php
<?php

namespace {Vendor}\{Module}\Plugin;

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

class {PluginName}
{
    public function after{MethodName}({ClassName} $subject): {type}
    {
        // Code here
    }
}
```

### Around

Evite usar plugins com métodos *around* quando eles não forem necessários, pois eles aumentam a *stack trace* e afetam a performance.

Antes da lista de argumentos do método observado, métodos *around* recebe um *callable* que permitirá a chamada do próximo método da cadeia. Quando o método executar o *callable*, o Magento chama o próximo plugin ou a função observada.

Se o método *around* não chamar o *callable*, ele prevenirá a execução de todos os seguintes plugins da cadeia e o método observado.

Quando o método *around* envolve um método observado que possui argumentos, o método *around* deve aceitar esses argumentos e encaminhá-los ao invocar a chamada do *proceed*. Deve-se ter cuidado ao combinar os parâmetros padrões e seus *type hints* da assinatura do método observado.

Caso o método observado possua argumentos com valores padrões, devem ser inseridos os mesmos valores padrões dos argumentos no método *around*.

```php
<?php

namespace {Vendor}\{Module}\Plugin;

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

class {PluginName}
{
    public function around{MethodName}(
        {ClassName} $subject,
        callable $proceed
    ): {type} {
        // Code before here
        $proceed();
        // Code after here
    }
}
```

---

## 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.

```bash
rm -rf var/generation/
rm -rf generated/
php bin/magento setup:di:compile
php bin/magento cache:clean
php bin/magento flush
```

### Diretórios e Arquivos

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

```
- app/
  - code/
    - {Vendor}/
        - {Module}/
          - etc/
            - di.xml
            - module.xml
          - Plugin/
            - {PluginName}.php
          - registration.php
          - composer.json
```