Desenvolvimento

Plugin do Magento 2 não dispara? São 8 lugares onde ele não pega

O Magento não injeta código dentro do seu método: ele gera uma classe que estende a sua. Tudo que o PHP não deixa sobrescrever fica fora do alcance do plugin.

Por Roger Takemiya · Publicado em · 5 min de leitura

O código está lá, o di.xml está no lugar, você limpou o cache — e o plugin não roda. Antes de sair espalhando die() pelo módulo, confere se o método que você quer interceptar é um dos que o Magento simplesmente não consegue interceptar.

O motivo é bem mecânico. O Magento não injeta nada dentro do seu método. Ele gera uma classe Interceptor em generated/code/ que estende a classe alvo e sobrescreve o método público. Se o PHP não deixa estender ou sobrescrever, acabou a conversa.

Os 8 casos em que o plugin não roda

Essa é a lista da documentação da Adobe, sem invenção minha:

  • método final;
  • método de classe declarada final;
  • método não público (protected ou private);
  • método estático;
  • __construct e __destruct;
  • virtual type;
  • objeto instanciado antes de Magento\Framework\Interception subir;
  • classe que implementa Magento\Framework\ObjectManager\NoninterceptableInterface.

Os quatro primeiros são PHP puro: não dá para sobrescrever aquilo, ponto. Os quatro últimos é que costumam queimar tempo.

Repare no último: toda classe \Proxy que o Magento gera implementa a NoninterceptableInterface. Então plugin no proxy nunca é aplicado. O plugin na classe real continua valendo — mas se você apontou o di.xml para o proxy, não vai acontecer nada.

E o penúltimo pega quem tenta interceptar o que roda no bootstrap: a leitura do app/etc/env.php, a montagem do próprio ObjectManager, tudo que o framework precisa ter de pé antes de existir interceptação. Ali plugin não é o caminho — se você precisa mudar esse comportamento, o caminho é preference ou configuração.

Conferir se o Magento enxergou o seu plugin

Passou pela lista e o método é interceptável? Então a pergunta vira outra: o Magento chegou a registrar o plugin. Quem responde é o dev:di:info:

php bin/magento dev:di:info 'Magento\Catalog\Model\Product'

A saída traz Preference, os parâmetros do construtor, e duas tabelas: Plugins: e Plugins for the Preference:, com as colunas Plugin, Method e Type. Se o seu nome não aparece em nenhuma das duas, o problema é declaração — não é lógica.

Agora a pegadinha que mais me aparece em consultoria: sem o segundo argumento, o comando só olha a área global. Plugin declarado em etc/frontend/di.xml ou etc/adminhtml/di.xml não aparece ali. Peça a área:

php bin/magento dev:di:info 'Magento\Checkout\Model\Session' frontend
php bin/magento dev:di:info 'Magento\Sales\Model\Order' adminhtml

A segunda tabela também vale ouro: se você fez plugin numa interface e o alvo tem preference, é em Plugins for the Preference que ele vai aparecer.

Três erros de declaração que não geram aviso nenhum

Quando o plugin não aparece no dev:di:info, quase sempre é um destes:

Área errada. Plugin em etc/adminhtml/di.xml não roda no site. Se precisa valer nos dois lados, o arquivo é etc/di.xml.

Nome do método fora do camelCase. Para getName() os nomes válidos são beforeGetName, afterGetName e aroundGetName. Escreveu afterGetname? O Magento não reclama, só ignora.

Cache de configuração velho. Em produção, di.xml novo pede compilação:

php bin/magento setup:di:compile
php bin/magento cache:flush

E tem um quarto que não é de registro, é de execução: o after precisa devolver alguma coisa. Esqueceu o return $result e o método passa a devolver null para a loja inteira — o erro aparece longe do seu módulo e você perde a tarde.

Perguntas rápidas

Posso trocar o método para public só para conseguir fazer o plugin?

No seu próprio módulo, sim. Em classe do core, não: você estaria editando vendor, e some no próximo composer update. Nesse caso o caminho é interceptar o método público que chama o privado, ou usar preference.

O plugin aparece no dev:di:info mas continua não rodando. E agora?

Registro está certo, então olhe a execução. Confira o nome do método em camelCase, se algum outro plugin com around está engolindo a chamada sem invocar o proceed, e se a classe realmente é instanciada pelo ObjectManager naquele fluxo.

Preciso rodar setup:di:compile toda vez que mexo num plugin?

Em produção, sim, porque as classes interceptor são geradas na compilação. Em modo developer o Magento gera sob demanda e um cache:clean config normalmente basta.

Pra conferir na fonte

  1. Plugins (interceptors) — Adobe Developer
  2. Command-line reference: Commerce on-premises — Adobe Experience League
  3. Dependency injection — Adobe Developer

plugin interceptor di.xml

Precisa de um orçamento? Ficarei feliz em ajudar. Clique Aqui