Plugin vs Preference en Magento 2
Si queremos sobreescribir alguna funcionalidad de Magento 2, lo primero que se nos viene a la cabeza es, ¿uso un “plugin” o un “preference”?
Para entenderlo mejor, voy a explicar para qué sirve cada una de las opciones. (Cogeré como ejemplo un módulo que sea JLNarvaez_OverrideModule
)
Preference
Un “preference” nos permitirá sobreescribir una clase al completo. Es decir, nuestra clase heredará de la clase que queramos sobreescribir, y prevalecerá la nuestra a la hora de ejecutarse la misma.
¿Cómo declarar un “preference”?
Tendremos que indicarlo en el archivo <module>/etc/di.xml
de la siguiente manera:
app/code/JLNarvaez/OverrideModule/etc/di.xml
En el atributo for
indicaremos la clase que queremos sobreescribir y en el atributo type
nuestra clase que la sobreescribirá.
Después de esto, crearemos nuestra clase, que hemos indicado en el atributo type
en la ruta que hemos indicado anteriormente. En este caso será:
app/code/JLNarvaez/OverrideModule/Controller/Onepage/Success.php
Es importante que nuestra clase herede de la clase que vayamos a sobreescribir. En este caso heredaremos de \Magento\Checkout\Controller\Onepage\Success
.
En este caso, voy a implementar mi propio método execute()
.
Este es el método original:
Por ejemplo, voy a hacer que cuando haga la validación de session (en la segunda línea de la función), si no es correcta, en vez de llevar al carrito, nos lleve a la página principal de nuestra tienda. Este es el resultado de nuestra clase, con el método sobreescrito:
Con esto ya tendríamos nuestra clase sobreescrita. Solo nos quedaría ejecutar el comando bin/magento setup:di:compile
,
ya que cada vez que hagamos cambio en el fichero di.xml
tendremos que ejecutar este comando.
Plugin
Un “plugin” (o también conocido como “interceptor”) nos permitirá interceptar una función de una clase, haciendo que un código se ejecute “antes” (before), “después” (after) o “antes y después” (around).
¿Cómo declarar un “plugin”?
También tendremos que indicarlo en el archivo <module>/etc/di.xml
de la siguiente manera:
En este caso en el atributo name
del elemento <type>
pondremos la clase de la cuál queramos crear un plugin.
Dentro del elemento <type>
tenemos un elemento <plugin>
donde pondremos un atributo name
que tendrá como
valor un nombre único con el que identificaremos a nuestro plugin.
En el atributo type
pondremos la clase en la cual vamos a crear nuestro plugin.
También tenemos otros atributos como el sortOrder
que nos servirá por si tenemos varios plugins que apunten a la
misma clase, establecer un orden de ejecución de los mismos. También tendremos la posibilidad de establecer si este
plugin lo queremos deshabilitar pasandole el atributo disabled
con valor true
. (Esto nos será útil por si queremos
desactivar algún Plugin en concreto, sin necesidad de tener que borrar la declaración por si en algún futuro nos
volviera a servir).
A la hora de crear el plugin, no tendremos que extender de ninguna clase y simplemente tendremos que crear nuestra clase
con el método que queramos “sobreescribir”. En este caso queremos usar el método execute()
para nuestro plugin. En este
momento decidiremos si nuestra lógica se ejecutará antes del método, después o antes y después.
Vamos a ver los tres casos:
beforeExecute()
Este método recibirá como primer parámetro la clase sobre la cual estamos haciendo el plugin (La clase del método original). Desde el segundo parámetro en adelante, serán los parámetros que tenga el método original. En este caso, el método execute original no recibe ningún parámetro.
afterExecute()
Este método recibirá como primer parámetro la clase sobre la cual estamos haciendo el plugin (La clase del método original). Como segundo parámetro recibirá el resultado de la función original. Desde el tercer parámetro en adelante, serán los parámetros que tenga el método original. En este caso, el método execute original no recibe ningún parámetro.
aroundExecute()
Este método recibirá como primer parámetro la clase sobre la cual estamos haciendo el plugin (La clase del método original). Como segundo parámetro recibirá un Callable con la función original (¡Es importante que llamemos a esta función para que se ejecute la función original!) Desde el tercer parámetro en adelante, serán los parámetros que tenga el método original. En este caso, el método execute original no recibe ningún parámetro.
Tras crear nuestra clase, y declararlo en el di.xml
, al igual que al hacer el preference, deberemos de
ejecutar el comando bin/magento setup:di:compile
.
¿Qué es preferible usar?
Evitaremos usar el preference en la medida de lo posible e intentaremos usar los plugins siempre que podamos. Pero, ¿Por qué?
En el caso de los plugin respetaremos las funciones originales y nuestro código no afectará al código original. Sin embargo, en el caso de los preference estamos machacando todo el código de la función. El problema de esto es que, por ejemplo, una actualización de Magento que cambie el código de este método, hará que nos perdamos estas nuevas cosas que se añadan, ya que el método de nuestra clase se ejecutará por encima del código original.
El problema de crear plugins es que tiene unas cuantas limitaciones. No se podrán crear plugins sobre lo siguiente:
- Métodos no públicos.
- Clases “final”
- Métodos “final”
- Métodos __construct y __destruct
- Métodos estáticos
- Virtual types
También se tendrá que dar el caso de que nuestra lógica sea imposible implementarla a través de un plugin, e implique cambios en varias líneas de código del método original.