Magento 2: utilizzare o non utilizzare direttamente l'ObjectManager?


134

Ok, quindi ieri abbiamo avuto un grande colloquio con altre persone della comunità Magento sull'uso diretto delle ObjectManagerclassi / template in .

Sono già a conoscenza dei motivi per cui non dovremmo usare direttamente l'ObjectManager, citando Alan Kent :

Ci sono diverse ragioni. Il codice funzionerà, ma è consigliabile non fare riferimento direttamente alla classe ObjectManager.

  • Perché lo diciamo noi! ;-) (meglio espresso come codice coerente è un buon codice)
  • Il codice potrebbe essere utilizzato in futuro con un framework di iniezione delle dipendenze diverso
  • Il test è più semplice : passi argomenti falsi per la classe richiesta, senza dover fornire un ObjectManager fittizio
  • Mantiene le dipendenze più chiare - è ovvio da cosa dipende il codice tramite l'elenco dei costruttori, piuttosto che avere dipendenze nascoste nel mezzo del codice
  • Incoraggia i programmatori a pensare meglio a concetti come l'incapsulamento e la modularizzazione : se il costruttore diventa grande, forse è un segno che il codice deve essere sottoposto a refactoring

Da quello che ho visto in StackExchange, molte persone tendono ad optare per la soluzione facile / breve / non raccomandata, ad esempio qualcosa del genere:

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

Invece di passare attraverso il processo doloroso ma raccomandato di:

  • creando un modulo
  • dichiarando le preferenze
  • iniettare dipendenze
  • dichiarare un metodo pubblico

Tuttavia, e qui arriva il dilemma, i file core di Magento 2 spesso chiamano direttamente l'ObjectManager . Un rapido esempio può essere trovato qui: https://github.com/magento/magento2/blob/develop/app/code/Magento/GoogleOptimizer/Block/Adminhtml/Form.php#L57

Quindi, ecco le mie domande:

  • Perché Magento sta facendo ciò che ci consigliano di non fare? Ciò significa che ci sono alcuni casi in cui dovremmo usare ObjectManagerdirettamente ? In tal caso, quali sono questi casi?
  • Quali sono le conseguenze dell'utilizzo diretto dell'ObjectManager ?


3
Link pertinente: mwop.net/blog/2016-04-26-on-locators.html . La parte rilevante sarebbe The intent of zend-servicemanager is for use as an Inversion of Control container. It was never intended as a general purpose service locator [...]. Che si applica anche a M2. Controlla anche la There are valid use casessezione, che, di nuovo, si applica anche qui.
nevvermind,

3
Ci fu un certo periodo di sviluppo di M2 quando OM era già lì, ma l'intero magento non era ancora cambiato per usare l'iniezione del costruttore. A quel punto molte persone hanno sostituito Mage :: getSingleton () con ObjectManager :: getInstance () -> get (). La maggior parte di tali usi furono introdotti in quel periodo. Successivamente tutte le chiamate Mage :: getSingleton () sono state sostituite con l'iniezione del costruttore da uno strumento, ma lo strumento non ha riconosciuto ObjectManager :: getInstance (), quindi non lo ha sostituito con l'iniezione del costruttore.
Anton Kril,

3
Possibile duplicato dell'istanza
Teja Bhagavan Kollepara,

3
@TejabhagavanKollepara hai letto entrambe le domande? Ci sono simili ma lungi dall'essere duplicati l'uno dall'altro
Raffaello al Pianismo digitale il

Risposte:


98

Non utilizzare direttamente l'ObjectManager!

Le eccezioni alla regola sono:

  • in metodi magici statiche come __wakeup, serialize, ecc
  • nel caso in cui dovessi rendere retrocompatibile il costruttore
  • nell'ambito globale, come negli apparecchi del test di integrazione.
  • in classe che necessita solo per la creazione di oggetti come factory, proxy, ecc

2
So che non dovrei mai usarlo direttamente, ma perché Magento lo sta facendo? ^^
Raffaello al Pianismo digitale

2
nel tuo esempio è per la compatibilità con le versioni precedenti
KAndy,

Quelli sono sempre contrassegnati come @deprecated?
Raffaello al Pianismo digitale,


5
oh sì amico, lo so che è solo confuso. Forse avrebbero dovuto dire "non farlo, ma
tieni

53

Quindi perché M2 a volte accede direttamente al gestore oggetti quando lo sconsigliamo?

Risposta brutale: M2 è una porta di M1, non una riscrittura completa. Quindi non dare per scontato che tutto il codice M2 sia ancora perfettamente portato (purtroppo). Solo perché trovi qualcosa nella base di codici M2, ciò non significa "è il modo migliore per farlo". A volte è solo "non siamo ancora riusciti a risolverlo".

Meno brutale: come per altre risposte, a volte DEVI usarlo perché non c'è alternativa. Altre volte potrebbe essere per motivi di compatibilità all'indietro. E il codice framework a volte ha senso usarlo direttamente, perché è un codice framework. Ma se dovessi indovinare senza guardare il codice, molti dovrebbero davvero essere risolti, ma non è stata ancora abbastanza alta priorità per farlo.

Basta ricordare il buon consiglio dei genitori: "Bambini, fate quello che dico, non quello che faccio!"


9
citazione eccellente: bambini, fate quello che dico, non quello che faccio!
sivakumar,

Non è così che funziona
kiddo

Esiste un modo per Magento 2 di consigliare di avere un problema di dipendenza soft senza gestore oggetti? Ho un modulo con una dipendenza leggera da un altro (carica un'altra classe se il modulo esiste). Non riesco a DI quella classe perché allora DI fallirà. Non posso nemmeno DI una Factory per quella classe perché la Factory non riuscirà a DI.
Nathan Merrill,

51

Non dovresti mai usare \Magento\Framework\App\ObjectManager::getInstance().
Sconfigge lo scopo dell'iniezione di dipendenza. Siamo tornati a Mage::getModel().
Il gestore oggetti dovrebbe essere usato solo nelle fabbriche e poi iniettato in un costruttore.

Il vantaggio di usare questo è meno codice da scrivere. Ma questo non lo rende OK.
Il fatto che questo sia ancora usato nel core, è perché non è stato ancora refactored. Spero che lo sia.


5
Quindi siamo entrambi d'accordo sul fatto che il codice Magento stia sbagliando, giusto?
Raffaello al Pianismo digitale,

11
giusto. si sbagliano :).
Marius

Non penso che stiano usando male. Lo stanno usando quando necessario: quando è necessaria la risoluzione dinamica (plugin, in particolare) e quando mantengono BC su metodi immediatamente deprecati.
nevvermind,

2
@nevvermind Utilizzando una fabbrica. Si utilizza di.xmlper creare una mappa di nome chiave => classe e iniettare quella mappa nel costruttore della fabbrica e utilizzare la fabbrica per creare un'istanza della classe tramite objectmanager
Marius

2
@nevvermind Ma l'opinione di un dipendente Magento supera la tua opinione. Hai una risposta sopra da KAndy che afferma in grassetto "non dovresti usare direttamente il gestore oggetti": magento.stackexchange.com/a/117103/146 Immagino che questo tipo di elimini la nebbia sul problema.
Marius

22

Perché Magento sta facendo ciò che ci consigliano di non fare? Ciò significa che ci sono alcuni casi in cui dovremmo usare direttamente l'ObjectManager? In tal caso, quali sono questi casi?

Senza conoscere la storia completa qui è la mia ipotesi:

Durante lo sviluppo di M2 del team di Magento a un certo punto ha uno script automatico che ha sostituito le occorrenze di Mage:getModel(), Mage::getSingleton(), $layout->createBlock(), ecc per usare l'ObjectManager.

Il refactoring successivo avrebbe dovuto risolvere il problema in modo da utilizzare invece l'iniezione di dipendenza corretta, ma non c'era tempo / risorse sufficienti per convertire tutte le occorrenze.

Anche il team Magento di recente sembra utilizzarlo come meccanismo di fuga. Invece di interrompere un'implementazione esistente (necessitando di cambiare il costruttore) semplicemente nascondono la nuova dipendenza tramite l'ObjectManager. Non posso dire di essere d'accordo con questo approccio: scrivere codice peggiore per evitare un'interruzione BC.

Quali sono le conseguenze dirette dell'utilizzo diretto dell'ObjectManager?

Penso che la tua domanda includa già abbastanza ragioni. Generalmente crea una dipendenza nascosta, in altre parole la dipendenza è nei dettagli dell'implementazione e non è visibile solo dal costruttore.


È ironico perché se lo avessero fatto correttamente prima di rilasciare al pubblico il BC non sarebbe stato affatto un problema
Robbie Averill

12

Non utilizzare direttamente l'Object Manager!

Per esempio:

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

anche se lavori con osservatori di eventi o plugin, non dovresti mai usarlo direttamente.

Potresti usarlo in Fabbriche, ma a parte il fatto che dovresti prima iniettare l'Object Manager nel costruttore, quindi puoi usare il suo oggetto nel tuo metodo

Preferibilmente usare:

1) dichiarare oggetto privato:

private $_objectManager;

2) iniettare nel costruttore e inizializzare:

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

3) utilizzare in qualche metodo:

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

Questa risposta è per le versioni inferiori di Magento 2.2, quindi prendi nota. Secondo i nuovi standard di Magento 2 ora non possiamo usare nemmeno l'istanza di objectManager. Dobbiamo utilizzare factory della classe oggetto o repository per ottenere qualsiasi dato.


È una buona pratica usarlo in questo modo?
enrico69,

Sì, perché magento non consente di utilizzare direct objectManager, quindi è necessario utilizzare in questo modo!
Ronak Chauhan,

Inoltre non dovresti mai usarlo in eventi (immagino che intendi osservatori) e plugin. È necessario iniettare gli oggetti necessari, non l'ObjectManager. Solo in una fabbrica puoi usare l'ObjectManager e quindi dovresti effettivamente iniettarlo invece di chiamare::getInstance()
7ochem

Bene, modifica la risposta @ 7ochem
Chauhan

ridimensionare qualsiasi risposta non è un modo appropriato, se hai una conoscenza migliore allora puoi aggiungere la tua risposta o puoi modificare la risposta di qualsiasi altra per avere un'idea migliore e utile per gli altri. @ 7ochem
Chauhan,

10

Il motivo principale per cui gli sviluppatori sono fortemente scoraggiati dall'utilizzare direttamente l'Object Manager è che l'uso diretto dell'Object Manager fa sì che l'estensione non sia installabile in modalità di rilascio compilata.

Quindi si rompe per i tuoi clienti utilizzando la modalità di rilascio, inclusi tutti i clienti su Magento Cloud.

Sembra che una percentuale ragionevolmente grande di sviluppatori (circa il 75%) non collauda le proprie estensioni per vedere se possono essere installati in modalità di rilascio, quindi non si imbattono nei problemi posti dall'uso errato di ObjectManager.

A partire dal 2017, il Marketplace Magento esegue un test di compilazione e installazione su tutte le estensioni vendute attraverso di esso. Se l'estensione utilizza direttamente l'Object Manager, non supererà questi test e verrà rifiutata dal Marketplace fino a quando non risolverai questo problema e lo ricaricherai.


2

È possibile provare creando un oggetto di objectManager e non utilizzare direttamente ObjectManager .

Usa qualcosa come

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*/
        }
    }
}

2
Se il gestore oggetti è un singleton, perché questo farebbe la differenza?
domdambrogia,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.