Magento 2: używać lub nie używać bezpośrednio menedżera obiektów ObjectManager?


134

Ok, więc wczoraj rozmawialiśmy z innymi ludźmi ze społeczności Magento na temat bezpośredniego wykorzystania ObjectManagerklas / szablonów .

Jestem już świadomy powodów, dla których nie powinniśmy bezpośrednio używać ObjectManager, cytując Alana Kenta :

Jest kilka powodów. Kod będzie działał, ale najlepiej jest nie odwoływać się bezpośrednio do klasy ObjectManager.

  • Ponieważ tak mówimy! ;-) (lepiej wyrażony jako spójny kod to dobry kod)
  • W przyszłości kod może być używany z inną strukturą wstrzykiwania zależności
  • Testowanie jest łatwiejsze - przekazujesz fałszywe argumenty dla wymaganej klasy, bez konieczności podawania próbnego menedżera obiektów ObjectManager
  • Utrzymuje jaśniejsze zależności - oczywiste jest, od czego zależy kod za pośrednictwem listy konstruktorów, a nie ukrywanie zależności w środku kodu
  • Zachęca programistów do lepszego myślenia o koncepcjach takich jak enkapsulacja i modularyzacja - jeśli konstruktor się powiększy, być może jest to znak, że kod wymaga refaktoryzacji

Z tego, co widziałem w StackExchange, wiele osób wybiera proste / krótkie / niezalecane rozwiązanie, na przykład coś takiego:

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

Zamiast przechodzić przez bolesny, ale zalecany proces :

  • tworzenie modułu
  • deklarowanie preferencji
  • wstrzykiwać zależności
  • zadeklarować metodę publiczną

Jednak i tu pojawia się dylemat, podstawowe pliki Magento 2 często wywołują bezpośrednio ObjectManager . Szybki przykład można znaleźć tutaj: https://github.com/magento/magento2/blob/develop/app/code/Magento/GoogleOptimizer/Block/Adminhtml/Form.php#L57

Oto moje pytania:

  • Dlaczego Magento robi to, czego od nas nie zaleca? Czy to oznacza, że istnieją przypadki, w których powinniśmy użyć ObjectManagerbezpośrednio ? Jeśli tak, jakie to są przypadki?
  • Jakie są konsekwencje bezpośredniego korzystania z ObjectManager ?


3
Odpowiedni link: mwop.net/blog/2016-04-26-on-locators.html . Odpowiednia część to The intent of zend-servicemanager is for use as an Inversion of Control container. It was never intended as a general purpose service locator [...]. Dotyczy to również M2. Sprawdź także There are valid use casessekcję, która również tutaj dotyczy.
nevvermind

3
Był pewien okres rozwoju M2, kiedy OM już tam był, ale całe magento nie zostało jeszcze zmienione, aby użyć iniekcji konstruktora. W tym momencie wiele osób zamieniło Mage :: getSingleton () na ObjectManager :: getInstance () -> get (). Większość takich zastosowań wprowadzono w tym okresie. Później wszystkie wywołania Mage :: getSingleton () zostały zastąpione przez wstrzyknięcie konstruktora przez narzędzie, ale narzędzie nie rozpoznało obiektu ObjectManager :: getInstance (), więc nie zastąpiło go wstrzyknięciem konstruktora.
Anton Kril,


3
@TejabhagavanKollepara czy przeczytałeś oba pytania? Istnieją podobne, ale dalekie od siebie duplikaty
Raphael w Digital Pianism

Odpowiedzi:


98

Nie należy bezpośrednio używać ObjectManager!

Wyjątkiem od reguły są:

  • w statycznych magicznych metod, takich jak __wakeup, serializeitp
  • w przypadku, gdy powinieneś zrobić wsteczną kompatybilność konstruktora
  • w zakresie globalnym, jak w urządzeniach testu integracji.
  • w klasie, która potrzebuje tylko do tworzenia takich obiektów, jak fabryka, serwer proxy itp

2
Wiem, że nigdy nie powinienem używać tego bezpośrednio, ale dlaczego Magento to robi? ^^
Raphael at Digital Pianism

2
w twoim przykładzie jest kompatybilność wsteczna
KAndy

Czy są one zawsze oznaczone jako @deprecated?
Raphael at Digital Pianism


5
o tak, kolego wiem, że to jest po prostu mylące. Może powinni byli powiedzieć: „nie rób tego, ale pamiętaj, że prawdopodobnie zostawiliśmy kilka błędów tu i tam”;)
Raphael w Digital Pianism

53

Dlaczego więc M2 czasami czasami uzyskuje bezpośredni dostęp do menedżera obiektów, skoro odradzamy?

Brutalna odpowiedź: M2 to port M1 - nie jest to kompletne przepisanie. Nie zakładaj więc, że cały kod M2 jest jeszcze doskonale przeniesiony (niestety). To, że znajdziesz coś w bazie kodu M2, nie oznacza, że ​​„jest to najlepszy sposób na zrobienie tego”. Czasami jest to po prostu „jeszcze nie udało się tego naprawić”.

Mniej brutalny: tak jak w przypadku innych odpowiedzi, czasami MUSISZ go użyć, ponieważ nie ma alternatywy. Innym razem może to wynikać z kompatybilności wstecznej. A kod frameworka czasami ma sens z jego bezpośrednim użyciem, ponieważ jest to kod frameworka. Ale gdybym musiał zgadywać bez patrzenia na kod, wiele naprawdę powinno zostać naprawionych, ale nie był jeszcze wystarczająco wysoki priorytet, aby to zrobić.

Pamiętaj tylko o dobrych radach dla rodziców: „Dzieci, róbcie to, co mówię, a nie to, co robię!”


9
doskonały cytat: dzieci, róbcie to, co mówię, a nie to, co robię!
sivakumar

Nie tak to działa, kiddo
Ansyori

Czy istnieje Magento 2 zalecający sposób na problem miękkiej zależności bez menedżera obiektów? Mam moduł z miękką zależnością od innej (ładuje inną klasę, jeśli moduł istnieje). Nie mogę DI tej klasy, ponieważ DI nie powiedzie się. Nie mogę nawet DI fabryki dla tej klasy, ponieważ fabryka nie uda się DI.
Nathan Merrill

50

Nigdy nie powinieneś używać \Magento\Framework\App\ObjectManager::getInstance().
To przeczy celowi wstrzykiwania zależności. Wróciliśmy do Mage::getModel().
Menedżer obiektów powinien być używany tylko w fabrykach, a następnie wprowadzany do konstruktora.

Zaletą korzystania z tego jest mniej kodu do pisania. Ale to nie sprawia, że ​​jest OK.
Fakt, że jest on nadal używany w rdzeniu, ponieważ nie został jeszcze refaktoryzowany. Mam nadzieję, że będzie.


5
Więc oboje zgadzamy się, że kod Magento robi to źle, prawda?
Raphael at Digital Pianism

11
dobrze. oni są źli :).
Marius

Nie sądzę, żeby używali źle. Używają go, gdy jest to konieczne: gdy potrzebne jest dynamiczne rozstrzyganie (zwłaszcza wtyczki) i gdy trzymamy BC na przestarzałych metodach.
nevvermind

2
@nevvermind Korzystanie z fabryki. Używasz go di.xmldo stworzenia mapy nazw klas => i wstrzykujesz tę mapę do konstruktora fabryki, a następnie używasz fabryki do tworzenia instancji klasy za pomocą menedżera obiektów
Marius

2
@nevvermind Ale opinia pracownika Magento przewyższa Twoją opinię. Powyższa odpowiedź od KAndy brzmi pogrubioną czcionką „nie powinieneś bezpośrednio używać menedżera obiektów”: magento.stackexchange.com/a/117103/146 Wydaje mi się, że ten rodzaj usuwa mgłę w tej kwestii.
Marius

22

Dlaczego Magento robi to, czego od nas nie zaleca? Czy to oznacza, że ​​istnieją przypadki, w których powinniśmy bezpośrednio używać ObjectManager? Jeśli tak, jakie te przypadki?

Nie znając pełnej historii, zgaduję:

Podczas rozwoju M2 zespół Magento w pewnym momencie zabrakło zautomatyzowany skrypt, który zastąpił wystąpień Mage:getModel(), Mage::getSingleton(), $layout->createBlock()itp używać ObjectManager.

Później refaktoryzacja powinna była to naprawić, aby zamiast tego użyć właściwego wstrzykiwania zależności, ale nie było wystarczająco dużo czasu / zasobów, aby przekonwertować wszystkie wystąpienia.

Ostatnio zespół Magento wydaje się używać tego jako mechanizmu ucieczki. Zamiast przerywać istniejącą implementację (poprzez konieczność zmiany konstruktora), po prostu ukrywają nową zależność za pomocą ObjectManager. Nie mogę powiedzieć, że zgadzam się z tym podejściem - pisząc gorszy kod, aby uniknąć złamania BC.

Jakie są bezpośrednie konsekwencje bezpośredniego używania ObjectManager?

Myślę, że twoje pytanie zawiera już wystarczające powody. Generalnie tworzy ukrytą zależność, innymi słowy, zależność jest w szczegółach implementacji i nie jest widoczna dla samego konstruktora.


Jest to ironiczne, ponieważ gdyby zrobiło to właściwie przed upublicznieniem, BC nie byłby wcale problemem
Robbie Averill

12

Nie należy używać bezpośrednio Menedżera obiektów!

Na przykład:

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

także jeśli pracujesz z obserwatorami zdarzeń lub wtyczkami, nigdy nie powinieneś używać go bezpośrednio.

Możesz użyć go w Fabrykach, ale oprócz tego, że najpierw powinieneś wstrzyknąć Menedżer obiektów do Konstruktora, możesz użyć jego obiektu w swojej metodzie

Preferowane użycie:

1) zadeklaruj obiekt prywatny:

private $_objectManager;

2) wstrzyknąć konstruktor i zainicjować:

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

3) użyj w jakiejś metodzie:

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

Ta odpowiedź dotyczy wersji Magento 2.2 poniżej, więc proszę zanotować. Zgodnie z nowymi standardami Magento 2 nie możemy teraz używać nawet instancji objectManager. Musimy użyć fabryki klasy obiektu lub repozytorium, aby uzyskać jakiekolwiek dane.


Czy dobrą praktyką jest używanie go w ten sposób?
enrico69,

Tak, ponieważ magento nie pozwala na użycie bezpośredniego menedżera obiektów objectManager, więc musisz użyć tej metody!
Ronak Chauhan

Nigdy nie powinieneś używać go również w wydarzeniach (myślę, że masz na myśli obserwatorów) i wtyczkach. Powinieneś wstrzyknąć potrzebne obiekty, a nie ObjectManager. Tylko w fabryce można użyć menedżera obiektów ObjectManager, a następnie powinieneś wstrzyknąć go zamiast dzwonić::getInstance()
7ochem

Tak, edytuj odpowiedź @ 7ochem
Ronak Chauhan

zlekceważenie jakiejkolwiek odpowiedzi nie jest właściwym sposobem. Jeśli masz lepszą wiedzę, możesz dodać własną odpowiedź lub możesz edytować odpowiedź innej osoby, aby uzyskać lepszy pomysł i pomóc innym. @ 7ochem
Ronak Chauhan

10

Głównym powodem, dla którego programiści są mocno zniechęceni do bezpośredniego korzystania z Menedżera obiektów, jest to, że bezpośrednie użycie Menedżera obiektów powoduje, że rozszerzenia nie można zainstalować w trybie kompilacji.

W związku z tym dla klientów korzystających z trybu zwolnienia, w tym wszystkich klientów korzystających z Magento Cloud, przestaje działać.

Wygląda na to, że dość duży odsetek programistów (około 75%) nie testuje swoich rozszerzeń, aby sprawdzić, czy można je zainstalować w trybie wydania, więc nie napotykaj problemów związanych z nieprawidłowym użyciem ObjectManager.

Od 2017 r. Magento Marketplace przeprowadza test kompilacji i instalacji na wszystkich sprzedawanych za jego pomocą rozszerzeniach. Jeśli twoje rozszerzenie korzysta bezpośrednio z Object Managera, nie przejdzie tych testów i zostanie odrzucone z Marketplace, dopóki nie rozwiążesz tego problemu i przeładujesz.


2

Możesz spróbować, tworząc obiekt objectManager i nie powinieneś bezpośrednio używać objectManager .

Użyj czegoś takiego jak

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
Jeśli menedżer obiektów jest singletonem, dlaczego miałoby to mieć znaczenie?
domdambrogia
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.