Magento 2: Plugin antes / próximo / depois da interação

32

No Magento 2, quando você cria um plug-in "around"

public function aroundRenderResult(
    \Magento\Framework\Controller\ResultInterface $subject,
    \Closure $proceed,
    ResponseHttp $response
) {
    //...
    $proceed($response);
    //...      
}    

você pode prosseguir para o próximo plug-in, culminando em chamar o método original real, chamando / chamando o $proceedmétodo passado . Esse é um padrão de design comum, geralmente visto nas implementações de middleware do PHP Frameworks.

Contudo - apresenta alguma confusão w / r / t aos detalhes da implementação. Especificamente

Se, além de um aroundPlugin, um objeto / classe tem um beforeou afterplugin definido, quando é acionado em relação à cadeia de plugins?

ou seja, todos os métodos anteriores são acionados antes que qualquer método de plug-in em torno seja acionado? Ou será antes plugins única fogo antes da final, reais verdadeira incêndios método?

O problema específico que estou tentando rastrear é que não consigo conectar um plug-in ao método de despacho no controlador frontal do Magento 2 quando o Magento está no modo de cache de página inteira . O cache da página inteira opera por um plug-in que não chama $proceed($response). Eu tentei cavar parte do código em torno desses plugins e achei o sistema difícil de raciocinar sem saber como seus plugins se destinam.

ie - a descrição na página dev docs parece, nesta instância específica, imprecisa. Não está claro se a documentação está incorreta ou se é um bug recentemente introduzido, se é um caso de extrema importância ou se a configuração do meu plugin está errada.

Alguém sabe, por observação direta ou por conhecimento cultural, como essa priorização deve funcionar?

Alan Storm
fonte
Alan, você tem uma regra de ouro quando usar \closure $proceedvs. \callable $proceedem um plug-in? O documento oficial apenas menciona \callablee nunca toca \closure.
Thdan

Respostas:

38

Os plug-ins são classificados primeiro pela ordem de classificação e depois pelo prefixo do método.

Exemplo: para o método com 3 plugins (PluginA, PluginB, PluginC) com os seguintes métodos e sortOrder:

  • PluginA (sortOrder = 10)
    • beforeDispatch ()
    • afterDispatch ()
  • PluginB (sortOrder = 20)
    • beforeDispatch ()
    • aroundDispatch ()
    • afterDispatch ()
  • PluginC (sortOrder = 30):
    • beforeDispatch ()
    • aroundDispatch ()
    • afterDispatch ()

O fluxo de execução deve ser o seguinte:

  • PluginA :: beforeDispatch ()
  • PluginB :: beforeDispatch ()
  • PluginB :: aroundDispatch ()
    • PluginC :: beforeDispatch ()
    • PluginC :: aroundDispatch ()
      • Ação :: dispatch ()
    • PluginC :: afterDispatch ()
  • PluginB :: afterDispatch ()
  • PluginA :: afterDispatch ()
Anton Kril
fonte
16

No livro de receitas Magento 2:

Se houver vários plugins que estendem a mesma função original, eles serão executados na seguinte sequência:

  • o plug-in anterior com o menor sortOrder
  • o plugin around com o menor sortOrder
  • outro antes de plugins (do menor para o maior sortOrder)
  • outro em torno de plugins (do menor para o maior sortOrder)
  • o plugin after com o mais alto sortOrder
  • outro após plugins (do mais alto para o mais baixo sortOrder)
Raphael na Digital Pianism
fonte
1

Para mim, deve funcionar como:

  • se a ordem de classificação não for definida, seu equivalente a zero (e isso significa que a ordem real é indefinida)
  • os plugins devem ser classificados por ordem

Se você revisar o código, \Magento\Framework\Interception\Interceptor::___callPlugins()poderá ver os plug-ins chamados em ordem de armazenados na $pluginInfovariável. Essas informações transmitidas do método gerado automaticamente em interceptores como

public function {method}()
{
    $pluginInfo = $this->pluginList->getNext($this->subjectType, '{method}');
    if (!$pluginInfo) {
        return parent::{method}();
    } else {
        return $this->___callPlugins('{method}', func_get_args(), $pluginInfo);
    }
}

Como você vê a \Magento\Framework\Interception\PluginListInterfaceinterface e a \Magento\Framework\Interception\PluginList\PluginListimplementação padrão responsáveis ​​pela classificação dos plugins. Consulte o método _inheritPlugins: 152

/**
 * Sort items
 *
 * @param array $itemA
 * @param array $itemB
 * @return int
 */
protected function _sort($itemA, $itemB)
{
    if (isset($itemA['sortOrder'])) {
        if (isset($itemB['sortOrder'])) {
            return $itemA['sortOrder'] - $itemB['sortOrder'];
        }
        return $itemA['sortOrder'];
    } elseif (isset($itemB['sortOrder'])) {
        return $itemB['sortOrder'];
    } else {
        return 1;
    }
} 

Para mim, essa função tem dois erros lógicos:

  • return $itemB['sortOrder'];deveria ser return - $itemB['sortOrder'];
  • return 1; deveria estar return 0;

Espero que ajude você.

KAndy
fonte
mas o $ pluginInfo está totalmente carregado com plugins? Ou há algum carregamento lento que possa afetar o comportamento? O que significa ordem de classificação para vários plugins? ou seja, é "antes do plug-in 1, em torno do plug-in 1, após o plug-in 1, antes do plug-in 2, em torno do plug-in 2, após o plug-in 2" ou "antes do plug-in 1", "antes do plug-in 2, em torno do plug-in1, em torno do plug-in 2", etc. O código se parece com o posterior, mas "getNext" preenche as informações do plug-in de uma maneira preguiçosa de carregamento, e como o Magento evita a recursão por aí torna tudo isso claro e difícil de identificar o que é um bug, o que é um recurso.
Alan Storm
Classe de plugin de classificação Magento, não método de plug-in.
Kandy
E a lista de plugins pode ser alterada, por exemplo, se uma nova ária for carregada.
precisa saber é o seguinte
Você tem algum conhecimento implícito que não é óbvio, porque "classificar a classe do plug-in e não o método do plug-in" não deixa claro quais são ou devem ser as regras para a interação do plug-in.
Alan Storm
talvez este link será útil magehero.com/posts/472/magento-2-interception
Kandy