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
Marius

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розділ, який, знову ж таки, стосується і тут.
неввермінд

3
Був певний період розробки М2, коли ОМ вже був там, але ціле магенто ще не було змінено на використання конструкторської інжекції. У цей момент багато людей замінили Mage :: getSingleton () на ObjectManager :: getInstance () -> get (). Більшість таких звичаїв були введені в той період. Пізніше всі виклики Mage :: getSingleton () були замінені інструментом конструктора, але інструмент не розпізнав ObjectManager :: getInstance (), тому він не замінив його введенням конструктора.
Антон Криль

3
Можливий дублікат примірника Magento 2 Helper
Teja Bhagavan Kollepara

3
@TejabhagavanKollepara ви читали обидва запитання? Є подібні, але далеко не дублікати один у одного
Рафаель у Digital Pianism

Відповіді:


98

Ви не повинні використовувати ObjectManager безпосередньо!

Виняток із правила:

  • в статичних магічних методів , таких як __wakeup, serializeі т.д.
  • у випадку, якщо вам слід зробити зворотну сумісність конструктора
  • в глобальному масштабі, як у складі інтеграційного тесту.
  • в класі, який потребує лише для створення об'єкта, такого як factory, proxy тощо

2
Я знаю, що ніколи не слід використовувати це безпосередньо, але чому Magento це робить? ^^
Рафаель у Digital Pianism

2
у вашому прикладі є зворотна сумісність
KAndy

Чи завжди вони позначені як @deprecated?
Рафаель у Digital Pianism


5
о так, приятелю, я знаю, що це просто заплутано. Можливо, вони повинні були сказати "не робіть цього, але пам'ятайте, що ми, мабуть, залишили тут і там деякі помилки";)
Рафаель у Digital Pianism

53

То чому M2 іноді звертається до менеджера об'єктів безпосередньо, коли ми рекомендуємо проти нього?

Брутальна відповідь: M2 - порт M1 - не повне перезапис. Тому не варто вважати, що весь код M2 досі ідеально перенесений (на жаль). Тільки тому, що ви знайдете щось у базі коду M2, це не означає, що "це найкращий спосіб зробити це". Іноді це просто "ми ще не дійшли до виправлення цього".

Менш жорстокий: Відповідно до інших відповідей, іноді ОБОВ'ЯЗКОВО використовувати його, як немає альтернативи. В інших випадках це може бути із зворотної причини сумісності. І рамковий код іноді має сенс використовувати його безпосередньо, тому що це рамковий код. Але якби мені довелося здогадуватися, не дивлячись на код, багато справді слід виправити, але це ще не було достатньо високим пріоритетом для цього.

Просто пам’ятайте добру пораду батьків: «Діти, робіть те, що я кажу, а не те, що я роблю!».


9
відмінна цитата: Діти, робіть те, що я кажу, а не те, що я роблю!
sivakumar

Це не так, як це працює kiddo
Ansyori

Чи рекомендований Magento 2 спосіб вирішити проблему м'якої залежності без менеджера об'єктів? У мене є модуль з м'якою залежністю від іншого (він завантажує інший клас, якщо модуль існує). Я не можу DI в цьому класі, тому що тоді DI не вдасться. Я навіть не можу зробити DI фабрикою для цього класу, оскільки завод не зможе зробити DI.
Натан Меррілл

50

Ніколи не слід використовувати \Magento\Framework\App\ObjectManager::getInstance().
Він перемагає мету введення залежності. Ми знову в Mage::getModel().
Менеджер об'єктів повинен використовуватися лише на заводах, а потім, як вводити їх у конструктор.

Перевага використання цього - менший код для запису. Але це не робить це нормально.
Справа в тому, що це все ще використовується в ядрі, це тому, що воно ще не було відновлено. Я сподіваюся, що буде.


5
Тож ми обидва погоджуємось, що код Magento робить це неправильно правильно?
Рафаель у цифровому піанізмі

11
правильно. вони помиляються :).
Маріус

Я не думаю, що вони неправильно використовують. Вони використовують його в разі необхідності: коли потрібне динамічне вирішення (особливо, плагіни) та при збереженні BC за методами негайно застарілих.
неввермінд

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. Я не можу сказати, що я згоден з таким підходом - писати гірший код, щоб уникнути розриву БК.

Які прямі наслідки використання 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 не дозволяє використовувати direct objectManager, тому вам доведеться використовувати цей спосіб!
Ronak Chauhan

Ви також ніколи не повинні використовувати його в подіях (я думаю, ви маєте на увазі спостерігачі) та плагінах. Ви повинні вводити потрібні вам об'єкти, а не ObjectManager. Тільки на заводі ви могли використовувати ObjectManager, а потім дійсно слід вводити його замість того, щоб дзвонити::getInstance()
7ochem

Право, редагуйте відповідь @ 7ochem
Ронак Чаухан

знизити будь-яку відповідь не є підходящим способом. Якщо ви володієте кращими знаннями, ви можете додати свою власну відповідь або ви можете редагувати відповідь будь-якого іншого, щоб отримати кращі уявлення та корисність для інших. @ 7ochem
Ronak Chauhan

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
Якщо менеджер об'єктів є одинарним, чому це має змінити?
domdambrogia
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.