Magento 2: использовать или не использовать ObjectManager напрямую?

134

Итак, вчера у нас была большая беседа с другими людьми из сообщества Magento относительно прямого использования ObjectManagerв классах / шаблонах .

Мне уже известны причины, по которым мы не должны использовать ObjectManager напрямую, цитируя Алана Кента :

Есть несколько причин. Код будет работать, но лучше не ссылаться на класс ObjectManager напрямую.

  • Потому что мы так говорим! ;-) (лучше выражать как непротиворечивый код - хороший код)
  • Код может быть использован с другой структурой внедрения зависимостей в будущем
  • Тестирование проще - вы передаете фиктивные аргументы для требуемого класса, не предоставляя фиктивный ObjectManager
  • Это делает ясность зависимостей - очевидно, от чего зависит код через список конструкторов, вместо того, чтобы скрывать зависимости в середине кода
  • Это побуждает программистов лучше продумывать такие понятия, как инкапсуляция и модульность - если конструктор становится большим, возможно, это знак того, что код нуждается в рефакторинге

Из того, что я видел в StackExchange, многие люди склонны выбирать простое / короткое / не рекомендуемое решение, например, что-то вроде этого:

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

Вместо того, чтобы пройти болезненный, но рекомендуемый процесс :

  • создание модуля
  • декларирование предпочтений
  • вводить зависимости
  • объявить публичный метод

Однако, здесь возникает дилемма: файлы ядра Magento 2 часто вызывают ObjectManager напрямую . Быстрый пример можно найти здесь: https://github.com/magento/magento2/blob/develop/app/code/Magento/GoogleOptimizer/Block/Adminhtml/Form.php#L57

Итак, вот мои вопросы:

  • Почему Magento делает то, что они рекомендуют нам не делать? Означает ли это, что в некоторых случаях мы должны использовать ObjectManagerнапрямую ? Если так, то каковы эти случаи?
  • Каковы последствия использования ObjectManager напрямую ?
Рафаэль в цифровом пианизме
источник
4
Проверьте это: magento.stackexchange.com/q/28617/146
Мариус
3
Соответствующая ссылка: mwop.net/blog/2016-04-26-on-locators.html . Соответствующая часть этого была бы The intent of zend-servicemanager is for use as an Inversion of Control container. It was never intended as a general purpose service locator [...]. Что касается и М2 тоже. Также проверьте There are valid use casesраздел, который, опять же, применяется и здесь.
nevvermind
3
Был некоторый период разработки M2, когда OM уже был там, но весь magento еще не был изменен, чтобы использовать инъекцию конструктора. В этот момент многие люди заменили Mage :: getSingleton () на ObjectManager :: getInstance () -> get (). Большинство таких обычаев были введены в тот период. Позже все вызовы Mage :: getSingleton () были заменены внедрением конструктора инструментом, но инструмент не распознал ObjectManager :: getInstance (), поэтому он не заменил его инъекцией конструктора.
Антон Криль
3
Возможный дубликат экземпляра Magento 2 Helper
Teja Bhagavan Kollepara
3
@TejabhagavanKollepara ты прочитал оба вопроса? Есть похожие, но далеко не дублирующие друг друга
Рафаэль в Digital

Ответы:

98

Вы не должны использовать ObjectManager напрямую!

Исключением из правила являются:

  • в статических магических методах, как __wakeup, serializeи т. д.
  • в случае, если вы должны сделать обратную совместимость конструктора
  • в глобальном масштабе, как в тестах интеграции.
  • в классе, который нужен только для создания таких объектов, как factory, proxy и т. д.
Канди
источник
2
Я знаю, что никогда не должен использовать это непосредственно, но почему Magento делает это? ^^
Рафаэль на цифровом пианизме
2
в вашем примере это для обратной совместимости
KAndy
Они всегда помечаются как @deprecated?
Рафаэль на цифровом пианизме
1
Как насчет этого: github.com/magento/magento2/blob/develop/app/code/Magento/… ?
Рафаэль на цифровом пианизме
5
о да, приятель, я знаю, это просто сбивает с толку. Возможно, им следовало бы сказать: «Не делай этого, но имей в виду, что мы, вероятно, оставили некоторые ошибки здесь и там»;)
Рафаэль на Digital Pianism
53

Так почему же M2 иногда напрямую обращается к диспетчеру объектов, когда мы против этого?

Брутальный ответ: M2 - это порт M1, а не полная перезапись. Так что не думайте, что весь код M2 прекрасно перенесен (к сожалению). Просто потому, что вы найдете что-то в базе кода M2, это не означает «это лучший способ сделать это». Иногда это просто «мы еще не успели это исправить».

Менее жестокий: Как и в других ответах, иногда вы ДОЛЖНЫ использовать его, так как альтернативы нет. В других случаях это может быть связано с обратной совместимостью. И фреймворковый код иногда имеет смысл использовать его напрямую, потому что это фреймворковый код. Но если бы мне пришлось угадывать, не глядя на код, многие действительно должны быть исправлены, но это еще не было достаточно высоким приоритетом, чтобы сделать это.

Просто помните хороший совет для родителей: «Дети, делайте то, что я говорю, а не то, что я делаю!»

Алан Кент
источник
9
отличная цитата: Дети, делайте то, что я говорю, а не то, что я делаю!
Сивакумар
Это не так, детка
Ansyori
Есть ли в Magento 2 рекомендуемый способ иметь проблему с мягкой зависимостью без менеджера объектов? У меня есть модуль с мягкой зависимостью от другого (он загружает другой класс, если модуль существует). Я не могу DI этот класс, потому что тогда DI потерпит неудачу. Я не могу даже DI фабрику для этого класса, потому что фабрика не сможет DI.
Натан Меррилл
51

Вы никогда не должны использовать \Magento\Framework\App\ObjectManager::getInstance().
Это побеждает цель введения зависимости. Мы вернулись в Mage::getModel().
Диспетчер объектов должен использоваться только на фабриках, а затем вводиться в конструктор.

Преимущество использования этого заключается в меньшем количестве кода для записи. Но это не делает это хорошо.
Факт, что это все еще используется в ядре, потому что это еще не подверглось рефакторингу. Я надеюсь, что это будет.

Мариус
источник
5
Таким образом, мы оба согласны, что код Magento делает это неправильно, не так ли?
Рафаэль на цифровом пианизме
11
правильно. они не правы :).
Мариус
Я не думаю, что они используют неправильно. Они используют его, когда это необходимо: когда требуется динамическое разрешение (особенно плагины) и когда BC сохраняет методы с немедленным осуждением.
nevvermind
2
@nevvermind Использование фабрики. Вы используете di.xmlдля создания карты имени ключа => и внедряете эту карту в конструктор фабрики и используете фабрику для создания экземпляра класса через objectmanager
Marius
2
@nevvermind Но мнение сотрудника Magento опережает ваше мнение. У вас есть ответ выше от KAndy, в котором жирным шрифтом написано, что «вы не должны использовать диспетчер объектов напрямую»: magento.stackexchange.com/a/117103/146 Я полагаю, что этот тип устраняет туман по этому вопросу.
Мариус
22

Почему Magento делает то, что они рекомендуют нам не делать? Означает ли это, что в некоторых случаях мы должны использовать ObjectManager напрямую? Если да, то каковы эти случаи?

Не зная полной истории, вот мое предположение:

При разработке М2 команда Magento на каком - то этапе побежала автоматизированный скрипт , который заменил вхождения Mage:getModel(), Mage::getSingleton(), $layout->createBlock()и т.д. , чтобы использовать ObjectManager.

Более поздний рефакторинг должен был исправить это, чтобы вместо этого использовать правильное внедрение зависимостей, но не было достаточно времени / ресурсов для преобразования всех вхождений.

Также в последнее время команда Magento, похоже, использует это как механизм побега. Вместо того, чтобы ломать существующую реализацию (нужно изменить конструктор), они просто скрывают новую зависимость через ObjectManager. Я не могу сказать, что согласен с таким подходом - писать худший код, чтобы избежать разрыва BC.

Каковы прямые последствия использования ObjectManager напрямую?

Я думаю, что ваш вопрос уже включает в себя достаточно причин. Обычно это создает скрытую зависимость, другими словами, зависимость находится в деталях реализации и не видна только из конструктора.

Кристоф в Фуман
источник
Это иронично, потому что если бы они сделали это должным образом, прежде чем выпускать публику, БК не был бы проблемой вообще
Робби Аверилл
12

Не следует использовать диспетчер объектов напрямую!

Например:

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

также, если вы работаете с наблюдателями событий или плагинами, вы никогда не должны использовать его напрямую.

Вы можете использовать его на фабриках, но за исключением того, что сначала вы должны внедрить диспетчер объектов в конструктор, затем вы можете использовать его объект в своем методе.

Предпочитают использовать:

1) объявить приватный объект:

private $_objectManager;

2) внедрить в конструктор и инициализировать:

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

3) использовать в некотором методе:

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

Этот ответ для версий ниже Magento 2.2, поэтому, пожалуйста, обратите внимание. В соответствии с новыми стандартами Magento 2 теперь мы не можем использовать даже экземпляр ObjectManager. Мы должны использовать фабрику класса объекта или репозитория для получения любых данных.

Ронак Чаухан
источник
Это хорошая практика, чтобы использовать это таким образом?
enrico69
Да, потому что magento не позволяет использовать прямой objectManager, поэтому вы должны использовать этот способ!
Ронак Чаухан
Вы также никогда не должны использовать его в событиях (я полагаю, вы имеете в виду Observers) и плагинах. Вы должны ввести нужные вам объекты, а не ObjectManager. Только на Фабрике вы можете использовать ObjectManager, и тогда вы действительно должны внедрить его вместо вызова::getInstance()
7ochem
Правильно, отредактируйте ответ @ 7ochem
Ронак Чаухан
понизить любой ответ - это неуместно. Если у вас есть более глубокие знания, вы можете добавить свой собственный ответ или отредактировать любой другой, чтобы лучше понять его и помочь другим. @ 7ochem
Ронак Чаухан
10

Основная причина, по которой разработчикам настоятельно не рекомендуется использовать диспетчер объектов напрямую, заключается в том, что прямое использование диспетчера объектов приводит к невозможности установки расширения в режиме скомпилированного выпуска.

Таким образом, он работает для ваших клиентов, использующих режим выпуска, в том числе для всех пользователей Magento Cloud.

Кажется, что довольно большая часть разработчиков (примерно 75%) не тестирует свои расширения, чтобы посмотреть, могут ли они быть установлены в режиме выпуска, поэтому не сталкивайтесь с проблемами, вызванными неправильным использованием ObjectManager.

Начиная с 2017 года, Magento Marketplace запускает тест на компиляцию и установку всех расширений, продаваемых через него. Если ваше расширение напрямую использует диспетчер объектов, оно не пройдёт эти тесты и будет отклонено из Marketplace, пока вы не решите эту проблему и не загрузите заново.

Деви Морган
источник
2

Вы можете попробовать создать объект objectManager и не следует использовать objectManager напрямую .

Используйте что-то вроде

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
Если диспетчер объектов является одноэлементным, почему это будет иметь значение?
Домдамброджа