Magento 2: Complemento antes / alrededor / después de la interacción

32

En Magento 2, cuando creas un complemento "alrededor"

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

puede continuar con el siguiente complemento, que culmina con llamar al método original real, llamando / invocando el $proceedmétodo pasado . Este es un patrón de diseño común, a menudo visto en implementaciones de middleware PHP Frameworks.

Sin embargo, presenta cierta confusión con respecto a los detalles de implementación. Específicamente

Si, además de un aroundPlugin, un objeto / clase tiene un beforeo un aftercomplemento definido, ¿ cuándo se activan en relación con la cadena de complementos circundantes?

es decir, ¿se activarán todos los métodos anteriores antes de que se active alguno de los métodos de complementos? ¿O antes de que los complementos solo se activen antes de que se active el método real real final ?

El problema específico que estoy tratando de rastrear es que parece que no puedo obtener un complemento adjunto al método de envío del controlador frontal Magento 2 cuando Magento está en modo de almacenamiento en caché de página completa . La caché de página completa funciona mediante un complemento de entorno que no llama $proceed($response). He intentado profundizar en algunos de los códigos que rodean estos complementos y he encontrado que es difícil razonar sobre el sistema sin saber cómo se supone que funcionan esos complementos.

es decir, la descripción en la página de documentos de desarrollo parece, en esta instancia específica, ser inexacta. No está claro si la documentación es incorrecta, o si se trata de un error introducido recientemente, si se trata de un caso límite o si la configuración de mi complemento es incorrecta.

¿Alguien sabe, por observación directa o por conocimiento cultural, cómo se supone que funciona esta priorización?

Alan Storm
fuente
Alan, ¿tienes una regla general cuando usar \closure $proceedvs. \callable $proceeden un complemento? El documento oficial solo menciona \callabley nunca toca \closure.
thdoan

Respuestas:

38

Los complementos se ordenan primero por orden de clasificación y luego por prefijo de método.

Ejemplo: para el método con 3 complementos (PluginA, PluginB, PluginC) con los siguientes métodos y sortOrder:

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

El flujo de ejecución debe ser el siguiente:

  • PluginA :: beforeDispatch ()
  • PluginB :: beforeDispatch ()
  • PluginB :: aroundDispatch ()
    • PluginC :: beforeDispatch ()
    • PluginC :: aroundDispatch ()
      • Acción :: dispatch ()
    • PluginC :: afterDispatch ()
  • PluginB :: afterDispatch ()
  • PluginA :: afterDispatch ()
Anton Kril
fuente
16

Del libro de cocina de Magento 2:

Si hay varios complementos que extienden la misma función original, se ejecutan en la siguiente secuencia:

  • el plugin anterior con el más bajo sortOrder
  • el complemento alrededor con el más bajo sortOrder
  • otros antes de los complementos (de menor a mayor sortOrder)
  • otros alrededor de complementos (de menor a mayor sortOrder)
  • el plugin after con el más alto sortOrder
  • otro después de complementos (del más alto al más bajo sortOrder)
Raphael en Digital Pianism
fuente
1

Para mí debería funcionar como:

  • si el orden de clasificación no está definido, su equivalente a cero (y esto significa que el orden real no está definido)
  • los complementos deben ordenarse por orden

Si revisa el código \Magento\Framework\Interception\Interceptor::___callPlugins(), puede ver que los complementos se invocan en orden de almacenamiento en $pluginInfovariable Esta información pasó del método generado automáticamente en 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 puede ver, la \Magento\Framework\Interception\PluginListInterfaceinterfaz y la \Magento\Framework\Interception\PluginList\PluginListimplementación predeterminada son responsables de la ordenación del complemento. Ver 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 mí, esta función tiene dos errores lógicos:

  • return $itemB['sortOrder'];debería ser return - $itemB['sortOrder'];
  • return 1; debiera ser return 0;

Espero que te ayude.

Kandy
fuente
pero $ pluginInfo está completamente cargado con complementos? ¿O hay alguna carga perezosa que podría afectar el comportamiento? ¿Qué significa el orden de clasificación para múltiples complementos? es decir, "antes del complemento 1, alrededor del complemento 1, después del complemento 1, antes del complemento 2, alrededor del complemento 2, después del complemento 2" o "antes del complemento 1", "antes del complemento 2, alrededor del complemento 1, alrededor del complemento 2", etc. El código se ve como el posterior, pero "getNext" completa la información del complemento en una forma de carga diferida (¿tal vez?) Y cómo Magento evita la recurrencia con todo esto hace que todo esto sea poco claro y difícil de detectar qué es un error, qué es una característica.
Alan Storm
La clase de complemento de clasificación de Magento no es un método de complemento.
KAndy
Y la lista de complementos se puede cambiar, por ejemplo, si se carga un nuevo aria.
KAndy
Hay un conocimiento implícito que tiene que no es obvio, porque "ordenar la clase de complemento y no el método de complemento" no deja en claro cuáles son o deberían ser las reglas para la interacción de complementos.
Alan Storm
tal vez este enlace sea útil magehero.com/posts/472/magento-2-interception
KAndy