Magento 2: ¿usar o no usar el ObjectManager directamente?

134

Ok, ayer tuvimos una gran conversación con otras personas de la comunidad de Magento sobre el uso directo de las ObjectManagerclases / plantillas .

Ya estoy al tanto de las razones por las que no deberíamos usar ObjectManager directamente, citando a Alan Kent :

Hay varias razones. El código funcionará, pero es una buena práctica no hacer referencia a la clase ObjectManager directamente.

  • ¡Porque nosotros lo decimos! ;-) (mejor expresado como código consistente es un buen código)
  • El código podría usarse con un marco de inyección de dependencia diferente en el futuro
  • La prueba es más fácil : pasa argumentos simulados para la clase requerida, sin tener que proporcionar un ObjectManager simulado
  • Mantiene las dependencias más claras : es obvio de qué depende el código a través de la lista de constructores, en lugar de tener dependencias ocultas en el medio del código
  • Alienta a los programadores a pensar mejor en conceptos como la encapsulación y la modularización : si el constructor crece, tal vez sea una señal de que el código necesita refactorización

Por lo que he visto en StackExchange, muchas personas tienden a buscar la solución fácil / corta / no recomendada, por ejemplo algo como esto:

<?php 
//Get Object Manager Instance
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();

//Load product by product id
$product = $objectManager->create('Magento\Catalog\Model\Product')->load($id);

En lugar de pasar por el doloroso pero recomendado proceso de:

  • creando un módulo
  • declarando preferencias
  • inyectar dependencias
  • declarar un método público

Sin embargo, y aquí viene el dilema, los archivos principales de Magento 2 a menudo llaman directamente al ObjectManager . Puede encontrar un ejemplo rápido aquí: https://github.com/magento/magento2/blob/develop/app/code/Magento/GoogleOptimizer/Block/Adminhtml/Form.php#L57

Asi que aqui están mis preguntas:

  • ¿Por qué Magento hace lo que nos recomiendan que no hagamos? ¿Eso significa que hay algunos casos en los que deberíamos usar el ObjectManagerdirectamente ? Si es así, ¿cuáles son esos casos?
  • ¿Cuáles son las consecuencias de usar ObjectManager directamente ?
Raphael en Digital Pianism
fuente
3
Enlace relevante: mwop.net/blog/2016-04-26-on-locators.html . Lo relevante sería The intent of zend-servicemanager is for use as an Inversion of Control container. It was never intended as a general purpose service locator [...]. Lo cual se aplica también a M2. También revise la There are valid use casessección, que, nuevamente, se aplica aquí también.
nevvermind
3
Hubo un período de desarrollo de M2 ​​cuando OM ya estaba allí, pero el magento completo aún no se cambió para usar la inyección del constructor. En ese momento, muchas personas reemplazaron Mage :: getSingleton () con ObjectManager :: getInstance () -> get (). La mayoría de estos usos se introdujeron en ese período. Más tarde, todas las llamadas Mage :: getSingleton () fueron reemplazadas con inyección de constructor por una herramienta, pero la herramienta no reconoció ObjectManager :: getInstance (), por lo que no la reemplazó con inyección de constructor.
Anton Kril el
3
Posible duplicado de la instancia
Teja Bhagavan Kollepara
3
@TejabhagavanKollepara, ¿leíste ambas preguntas? Hay similares pero lejos de ser duplicados entre sí
Raphael en Digital Pianism

Respuestas:

98

¡No deberías usar el ObjectManager directamente!

Las excepciones a la regla son:

  • en los métodos mágicos estáticos como __wakeup, serialize, etc.
  • en caso de que debas hacer una compatibilidad hacia atrás del constructor
  • en alcance global, como en accesorios de prueba de integración.
  • en clase que solo necesita para la creación de objetos como fábrica, proxy, etc.
Kandy
fuente
2
Sé que nunca debería usarlo directamente, pero ¿por qué lo está haciendo Magento? ^^
Raphael en Digital Pianism
2
en su ejemplo es para la compatibilidad con versiones anteriores
Kandy
¿Están marcados siempre como @deprecated?
Raphael en Digital Pianism
1
¿Qué pasa con este: github.com/magento/magento2/blob/develop/app/code/Magento/… ?
Raphael en Digital Pianism
55
oh sí amigo, sé que es confuso. Tal vez deberían haber dicho "no lo hagas, pero ten en cuenta que probablemente hayamos dejado algunos errores aquí y allá";)
Raphael en Digital Pianism
53

Entonces, ¿por qué M2 a veces accede al administrador de objetos directamente cuando recomendamos no hacerlo?

Respuesta brutal: M2 es un puerto de M1, no una reescritura completa. Por lo tanto, no asuma que todo el código M2 está perfectamente portado todavía (desafortunadamente). Solo porque encuentre algo en la base del código M2, eso no significa "es la mejor manera de hacerlo". A veces es simplemente "todavía no hemos podido arreglarlo".

Menos brutal: según otras respuestas, a veces DEBE usarlo ya que no hay alternativa. Otras veces puede ser por razones de compatibilidad con versiones anteriores. Y el código marco a veces tiene sentido usarlo directamente, porque es un código marco. Pero si tuviera que adivinar sin mirar el código, muchos realmente deberían repararse, pero aún no ha sido una prioridad lo suficientemente alta como para hacerlo.

Solo recuerde el buen consejo para padres: "¡Niños, hagan lo que digo, no lo que hago!"

Alan Kent
fuente
99
excelente cita: ¡Niños, hagan lo que digo, no lo que hago!
sivakumar
Así no funciona kiddo
Ansyori 01 de
¿Hay alguna forma recomendada de Magento 2 de tener un problema de dependencia flexible sin el administrador de objetos? Tengo un módulo con una dependencia suave de otro (carga otra clase si el módulo existe). No puedo DI esa clase porque entonces DI fallará. Ni siquiera puedo DI una Fábrica para esa clase porque la fábrica no podrá DI.
Nathan Merrill
51

Nunca deberías usar \Magento\Framework\App\ObjectManager::getInstance().
Derrota el propósito de la inyección de dependencia. Estamos de vuelta en Mage::getModel().
El administrador de objetos debe usarse solo en fábricas y luego como se inyecta en un constructor.

La ventaja de usar esto es menos código para escribir. Pero esto no lo hace correcto.
El hecho de que esto todavía se use en el núcleo es porque aún no se refactorizó. Yo espero que sea.

Marius
fuente
55
Entonces, ambos estamos de acuerdo en que el código de Magento lo está haciendo mal, ¿verdad?
Raphael en Digital Pianism
11
derecho. están equivocados :).
Marius
No creo que estén usando mal. Lo usan cuando es necesario: cuando se necesita una resolución dinámica (complementos, especialmente) y cuando se mantiene BC en métodos obsoletos inmediatamente.
nevvermind
2
@nevvermind Usando una fábrica. Se usa di.xmlpara crear un mapa de nombre de clase => e inyectar ese mapa en el constructor de la fábrica y usar la fábrica para crear una instancia de la clase a través del administrador de objetos
Marius
2
@nevvermind Pero la opinión de un empleado de Magento supera su opinión. Tiene una respuesta anterior de KAndy que dice en negrita "no debe usar el administrador de objetos directamente": magento.stackexchange.com/a/117103/146 Supongo que eso aclara la niebla sobre el tema.
Marius
22

¿Por qué Magento hace lo que nos recomiendan que no hagamos? ¿Eso significa que hay algunos casos en los que deberíamos usar ObjectManager directamente? Si es así, ¿cuáles son esos casos?

Sin saber la historia completa aquí es mi suposición:

Durante el desarrollo del M2 del equipo de Magento en algún momento corrió un script automatizado que sustituyó ocurrencias de Mage:getModel(), Mage::getSingleton(), $layout->createBlock(), etc, para utilizar el ObjectManager.

La refactorización posterior debería haber solucionado esto para usar la inyección de dependencia adecuada, pero no había suficiente tiempo / recursos para convertir todas las ocurrencias.

También el equipo de Magento últimamente parece usar esto como un mecanismo de escape. En lugar de romper una implementación existente (al necesitar cambiar el constructor), simplemente ocultan la nueva dependencia a través del ObjectManager. No puedo decir que esté de acuerdo con este enfoque: escribir un código peor para evitar una interrupción de BC.

¿Cuáles son las consecuencias directas de usar ObjectManager directamente?

Creo que su pregunta ya incluye suficientes razones. En general, crea una dependencia oculta, en otras palabras, la dependencia está en los detalles de implementación y no es visible solo desde el constructor.

Kristof en Fooman
fuente
Es irónico porque si lo hubiera hecho correctamente antes de lanzar al público el BC no habría sido un problema en absoluto
Robbie Averill
12

¡No debería usar el Administrador de objetos directamente!

Por ejemplo:

\Magento\Framework\App\ObjectManager::getInstance();

Además, si está trabajando con observadores de eventos o complementos, nunca debe usarlo directamente.

Puede usarlo en Fábricas, pero excepto que primero debe inyectar el Administrador de objetos en el Constructor y luego puede usar su objeto en su método

Preferido de usar:

1) declarar objeto privado:

private $_objectManager;

2) inyectar en el constructor e inicializar:

public function __construct(
    \Magento\Framework\ObjectManagerInterface $objectmanager
) {
    $this->_objectManager = $objectmanager;
}

3) uso en algún método:

public function create() {
    return $this->_objectManager->create(/* ......... */);
}

Esta respuesta es para las siguientes versiones de Magento 2.2, así que tome nota. Según los nuevos estándares de Magento 2 ahora, tampoco podemos usar incluso la instancia de objectManager. Tenemos que usar la fábrica de la clase de objeto o repositorio para obtener datos.

Ronak Chauhan
fuente
¿Es una buena práctica usarlo de esta manera?
enrico69
Sí, debido a que magento no permite usar objectManager directo, ¡así que debes usarlo de esta manera!
Ronak Chauhan
Tampoco debes usarlo en eventos (supongo que te refieres a observadores) y complementos. Debe inyectar los objetos que necesita, no el ObjectManager. Solo en una Fábrica podría usar el ObjectManager y luego debería inyectarlo en lugar de llamar::getInstance()
7ochem
Correcto, edite la respuesta @ 7ochem
Ronak Chauhan
Votar abajo cualquier respuesta no es una manera apropiada. Si tiene un mejor conocimiento, puede agregar su propia respuesta o puede editar la respuesta de cualquier otra para tener una mejor idea y ayudar a los demás. @ 7ochem
Ronak Chauhan
10

La razón principal por la que se desaconseja encarecidamente a los desarrolladores el uso directo del Administrador de objetos es que el uso directo del Administrador de objetos hace que la extensión no sea instalable en el modo de lanzamiento compilado.

Por lo tanto, se rompe para sus clientes que usan el modo de lanzamiento, incluidos todos los clientes en Magento Cloud.

Parece que una proporción razonablemente grande de desarrolladores (aproximadamente el 75%) no prueba sus extensiones para ver si pueden instalarse en modo de lanzamiento, por lo que no se tope con los problemas que plantea el uso incorrecto de ObjectManager.

A partir de 2017, Magento Marketplace ejecuta una prueba de compilación e instalación en todas las extensiones vendidas a través de él. Si su extensión utiliza el Administrador de objetos directamente, fallará estas pruebas y será rechazado del Mercado hasta que resuelva este problema y vuelva a cargarlo.

Dewi Morgan
fuente
2

Puede intentarlo creando un objeto de objectManager y no debe usar objectManager directamente .

Usa algo como,

class Example extends \Magento\Framework\View\Element\Template
{
    private $_objectManager;

    public function __construct(
        \Magento\Framework\ObjectManagerInterface $objectmanager
    ){
        $this->_objectManager = $objectmanager;
    }

    public function getExample()
    {
        $customerSession = $this->_objectManager->create("Magento\Customer\Model\Session");
        if ($customerSession->isLoggedIn()) {
            $customerData = $customerSession->getCustomer()->getData();
            /*Your logic*/
        }
    }
}
Kazim Noorani
fuente
2
Si el administrador de objetos es un singleton, ¿por qué esto haría una diferencia?
Domdambrogia 01 de