Magento 2: usar ou não usar o ObjectManager diretamente?

134

Ok, ontem tivemos uma grande conversa com outras pessoas da comunidade Magento sobre o uso direto do ObjectManagernas aulas / modelos .

Eu já estou ciente dos motivos pelos quais não devemos usar o ObjectManager diretamente, citando Alan Kent :

Existem várias razões. O código funcionará, mas é uma prática recomendada não fazer referência diretamente à classe ObjectManager.

  • Porque dizemos isso! ;-) (melhor expresso como código consistente é bom código)
  • O código pode ser usado com uma estrutura de injeção de dependência diferente no futuro
  • O teste é mais fácil - você passa argumentos simulados para a classe necessária, sem precisar fornecer um ObjectManager falso
  • Mantém as dependências mais claras - é óbvio do que o código depende via lista de construtores, em vez de ter dependências ocultas no meio do código
  • Incentiva os programadores a pensarem melhor em conceitos como encapsulamento e modularização - se o construtor crescer, talvez seja um sinal de que o código precisa ser refatorado

Pelo que vi no StackExchange, muitas pessoas tendem a procurar a solução fácil / curta / não recomendada, por exemplo, algo como isto:

<?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);

Em vez de passar pelo doloroso, mas recomendado, processo de:

  • criando um módulo
  • declarando preferências
  • injetar dependências
  • declarar um método público

No entanto, e aqui está o dilema, os arquivos principais do Magento 2 costumam chamar o ObjectManager diretamente . Um exemplo rápido pode ser encontrado aqui: https://github.com/magento/magento2/blob/develop/app/code/Magento/GoogleOptimizer/Block/Adminhtml/Form.php#L57

Então, aqui estão as minhas questões:

  • Por que o Magento está fazendo o que eles recomendam que não façamos? Isso significa que existem alguns casos em que devemos usar o ObjectManagerdiretamente ? Se sim, quais são esses casos?
  • Quais são as consequências de usar o ObjectManager diretamente ?
Raphael na Digital Pianism
fonte
4
Verifique isto: magento.stackexchange.com/q/28617/146
Marius
3
Link relevante: mwop.net/blog/2016-04-26-on-locators.html . A parte relevante disso seria The intent of zend-servicemanager is for use as an Inversion of Control container. It was never intended as a general purpose service locator [...]. O que também se aplica ao M2. Verifique também a There are valid use casesseção, que novamente se aplica aqui também.
Nevay
3
Houve algum período de desenvolvimento do M2 quando o OM já estava lá, mas o magento inteiro ainda não foi alterado para usar a injeção do construtor. Nesse ponto, muitas pessoas substituíram Mage :: getSingleton () por ObjectManager :: getInstance () -> get (). A maioria desses usos foi introduzida naquele período. Mais tarde, todas as chamadas Mage :: getSingleton () foram substituídas por injeção de construtor por uma ferramenta, mas a ferramenta não reconheceu ObjectManager :: getInstance (), portanto, não a substituiu por injeção de construtor.
Anton Kril 06/10
3
Possível duplicata da instância
Teja Bhagavan Kollepara
3
@TejabhagavanKollepara você leu as duas perguntas? Não são semelhantes, mas longe de ser duplicado entre si
Raphael em Digital pianismo

Respostas:

98

Você não deve usar o ObjectManager diretamente!

As exceções da regra são:

  • em métodos mágicos estáticos, como __wakeup, serialize, etc.
  • caso você deva fazer compatibilidade com versões anteriores do construtor
  • no escopo global, como em equipamentos de teste de integração.
  • na classe que precisa apenas para criação de objeto como factory, proxy, etc
KAndy
fonte
2
Eu sei que nunca devo usá-lo diretamente, mas por que o Magento está fazendo isso? ^^
Rafael no Digital Pianism
2
no seu exemplo é para compatibilidade com versões anteriores
KAndy 26/16
Esses são sempre sinalizados como @ obsoletos?
Raphael no Pianism Digital
1
E esse: github.com/magento/magento2/blob/develop/app/code/Magento/… ?
Raphael no Digital Pianism
5
oh sim, companheiro, eu sei que é apenas confuso. Talvez eles devessem ter dito "não faça isso, mas esteja ciente de que provavelmente deixamos alguns erros aqui e ali";)
Raphael no Digital Pianism
53

Então, por que o M2 às vezes acessa o gerenciador de objetos diretamente quando recomendamos?

Resposta brutal: M2 é uma porta do M1 - não uma reescrita completa. Portanto, não assuma que todo o código M2 ainda esteja perfeitamente portado (infelizmente). Só porque você encontra algo na base de código M2, isso não significa "é a melhor maneira de fazê-lo". Às vezes, é apenas "ainda não conseguimos corrigi-lo".

Menos brutal: como em outras respostas, às vezes você DEVE usá-lo, pois não há alternativa. Outras vezes, pode ser por motivos de compatibilidade com versões anteriores. E o código da estrutura às vezes faz sentido usá-lo diretamente, porque é um código da estrutura. Mas se eu tivesse que adivinhar sem olhar para o código, muitos realmente deveriam ser corrigidos, mas ainda não foi uma prioridade alta o suficiente.

Lembre-se do bom conselho para os pais: "Crianças, façam o que eu digo, não o que eu faço!"

Alan Kent
fonte
9
excelente citação: Crianças, faça o que eu digo, não o que eu faço!
precisa saber é o seguinte
Isso não é assim que funciona garoto
Ansyori
Existe uma maneira recomendada de Magento 2 para ter um problema de dependência leve sem o gerenciador de objetos? Eu tenho um módulo com uma dependência suave de outro (ele carrega outra classe, se o módulo existir). Não posso entrar nessa classe porque então falhará. Eu não posso nem DI uma fábrica para essa classe porque a fábrica falhará.
Nathan Merrill
50

Você nunca deve usar \Magento\Framework\App\ObjectManager::getInstance().
Ele anula o objetivo da injeção de dependência. Estamos de volta às Mage::getModel().
O gerenciador de objetos deve ser usado apenas em fábricas e depois como injetado em um construtor.

A vantagem de usar isso é menos código para escrever. Mas isso não faz tudo certo.
O fato de isso ainda ser usado no núcleo é porque ainda não foi refatorado. Espero que seja.

Marius
fonte
5
Então, nós dois concordamos que o código Magento está fazendo errado, certo?
Raphael no Pianism Digital
11
direito. eles estão errados :).
Marius
Eu não acho que eles estão usando errado. Eles estão usando quando necessário: quando a resolução dinâmica é necessária (plug-ins, especialmente) e ao manter o BC em métodos imediatamente obsoletos.
Nevegrind
2
@nevvermind Usando uma fábrica. Você usa di.xmlpara criar um mapa key => class name e injetar esse mapa no construtor da fábrica e usar a factory para instanciar a classe através do gerenciador de objetos
Marius
2
@nevvermind Mas a opinião de um funcionário da Magento supera sua opinião. Você tem uma resposta acima do KAndy que declara em negrito "você não deve usar o gerenciador de objetos diretamente": magento.stackexchange.com/a/117103/146 Acho que isso meio que limpa a névoa sobre o assunto.
Marius
22

Por que o Magento está fazendo o que eles recomendam que não façamos? Isso significa que existem alguns casos em que devemos usar o ObjectManager diretamente? Se sim, quais são esses casos?

Sem saber a história completa, aqui está o meu palpite:

Durante o desenvolvimento do M2 a equipe Magento em algum estágio correu um script automatizado que substituiu ocorrências Mage:getModel(), Mage::getSingleton(), $layout->createBlock(), etc, para usar o ObjectManager.

A refatoração posterior deveria ter corrigido isso para usar a injeção de dependência adequada, mas não havia tempo / recursos suficientes para converter todas as ocorrências.

Além disso, a equipe do Magento recentemente parece usar isso como um mecanismo de fuga. Em vez de interromper uma implementação existente (precisando alterar o construtor), eles simplesmente ocultam a nova dependência por meio do ObjectManager. Não posso dizer que concordo com essa abordagem - escrever código pior para evitar uma quebra de BC.

Quais são as consequências diretas do uso direto do ObjectManager?

Acho que sua pergunta já inclui motivos suficientes. Geralmente, ele cria uma dependência oculta; em outras palavras, a dependência está nos detalhes da implementação e não é visível apenas pelo construtor.

Kristof na Fooman
fonte
É irônico, porque tinha feito há-lo corretamente antes de liberar para o público o BC não teria sido um problema em tudo
Robbie Averill
12

Não deve usar o gerenciador de objetos diretamente!

Por exemplo:

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

Além disso, se você estiver trabalhando com observadores de eventos ou plug-ins, nunca deve usá-lo diretamente.

Você pode usá-lo em Fábricas, mas, exceto que você deve injetar o Gerenciador de Objetos no Construtor primeiro, então você pode usar o objeto em seu método

Preferido para usar:

1) declarar objeto privado:

private $_objectManager;

2) injete no construtor e inicialize:

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

3) use em algum método:

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

Esta resposta é para as versões abaixo do Magento 2.2, portanto, tome nota. De acordo com os novos padrões do Magento 2, agora não podemos usar nem mesmo a instância do objectManager. Temos que usar a fábrica da classe de objeto ou repositório para obter quaisquer dados.

Ronak Chauhan
fonte
É uma boa prática usá-lo dessa maneira?
Enrico69
Sim, porque o magento não permite usar o objectManager direto, então você deve usar desta maneira!
Ronak Chauhan
Você também nunca deve usá-lo em eventos (acho que você quer dizer Observadores) e plugins. Você deve injetar os objetos necessários, não o ObjectManager. Apenas em uma fábrica você poderia usar o ObjectManager e então você deve realmente injetá-lo em vez de chamar::getInstance()
7ochem
Certo, edite a resposta @ 7ochem
Ronak Chauhan 16/04
recusar qualquer resposta não é uma maneira apropriada. Se você tiver um conhecimento melhor, poderá adicionar sua própria resposta ou editar a resposta de qualquer outra pessoa para ter uma idéia melhor e ser útil para outras pessoas. @ 7ochem
Ronak Chauhan 16/04
10

O principal motivo pelo qual os desenvolvedores são fortemente desencorajados a usar o Gerenciador de Objetos diretamente é que o uso direto do Gerenciador de Objetos faz com que a extensão não seja instalável no modo de versão compilada.

Por isso, é importante para seus clientes usando o modo de liberação, incluindo todos os clientes no Magento Cloud.

Parece que uma proporção razoavelmente grande de desenvolvedores (aproximadamente 75%) não testa suas extensões para ver se elas podem ser instaladas no modo de liberação, portanto, não se deparem com os problemas causados ​​pelo uso incorreto do ObjectManager.

A partir de 2017, o Magento Marketplace executa um teste de compilação e instalação em todas as extensões vendidas através dele. Se sua extensão usar o Gerenciador de Objetos diretamente, ela falhará nesses testes e será rejeitada no Marketplace até você resolver esse problema e fazer o upload novamente.

Dewi Morgan
fonte
2

Você pode tentar criar um objeto de objectManager e não deve usá-lo diretamente .

Use 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
fonte
2
Se o gerenciador de objetos é um singleton, por que isso faria diferença?
domdambrogia