160

Ok, so yesterday we had a big talk with other people from the Magento community regarding the direct use of the ObjectManager in classes/templates.

I'm already aware of the reasons why we shouldn't use the ObjectManager directly, quoting Alan Kent :

There are several reasons. The code will work, but it is best practice to not reference the ObjectManager class directly.

  • Because we say so! ;-) (better expressed as consistent code is good code)
  • The code could be used with a different dependency injection framework in the future
  • Testing is easier - you pass in mock arguments for the required class, without having to provide a mock ObjectManager
  • It keeps dependencies clearer - it is obvious what the code depends on via constructor list, rather than having dependencies hidden in the middle of the code
  • It encourages programmers to think about concepts like encapsulation and modularization better - if the constructor gets big, maybe it is a sign the code needs refactoring

From what I've seen in StackExchange, a lot of people tend to go for the easy/short/not recommended solution for example something like this:

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

Instead of going through the painful but recommended process of:

  • creating a module
  • declaring preferences
  • inject dependencies
  • declare a public method

However, and here comes the dilemma, Magento 2 core files often call the ObjectManager directly. A quick example can be found here: https://github.com/magento/magento2/blob/develop/app/code/Magento/GoogleOptimizer/Block/Adminhtml/Form.php#L57

So here are my questions:

  • Why is Magento doing what they recommend us not to do ? Does that mean there are some cases where we should use the ObjectManager directly? If so, what are those cases?
  • What are the consequences of using the ObjectManager directly?
Rafael Corrêa Gomes
  • 13,309
  • 14
  • 84
  • 171
Raphael at Digital Pianism
  • 70,385
  • 34
  • 188
  • 352
  • at many places magento core team used directly Objectmanager concepts, then why we cant use it? – Rakesh Jesadiya May 26 '16 at 11:24
  • @Rakesh that's exactly my question ;) as we talked yesterday, I got concerned about that problem, I reckon this need clarification. – Raphael at Digital Pianism May 26 '16 at 11:25
  • 5
    Check this out: http://magento.stackexchange.com/q/28617/146 – Marius May 26 '16 at 11:29
  • 3
    Relevant link: https://mwop.net/blog/2016-04-26-on-locators.html. The relevant bit of it would be The intent of zend-servicemanager is for use as an Inversion of Control container. It was never intended as a general purpose service locator [...]. Which it applies to M2, too. Also check the There are valid use cases section, which, again, applies here, too. – nevvermind May 26 '16 at 12:29
  • Being scared of breaking things vs direct use of the ObjectManager is worse than I thought https://github.com/magento/magento2/commit/90f4decc16357dddfc91366d8b37f2668be18631 – Kristof at Fooman May 27 '16 at 14:59
  • 4
    There was some period of M2 development when OM was already there, but whole magento was not yet changed to use constructor injection. At that point many people replaced Mage::getSingleton() with ObjectManager::getInstance()->get(). Most of such usages were introduced at that period. Later all Mage::getSingleton() calls were replaced with constructor injection by a tool, but tool did not recognized ObjectManager::getInstance(), so it did not replace it with constructor injection. – Anton Kril Oct 06 '16 at 11:03
  • 3
  • 3
    @TejabhagavanKollepara did you read both questions ? There are similar but far from being duplicate from each other – Raphael at Digital Pianism Feb 20 '17 at 12:15
  • " if the constructor gets big, maybe it is a sign the code needs refactoring".. OK but what should be do when we have to extends a native MG2 class (it happens sometimes) which already have a huge constructo? – enrico69 Sep 29 '17 at 16:06
  • @enrico69 what does "MG2" mean? – grok_in_full Jul 26 '20 at 21:35
  • @grok_in_full Magento 2 – enrico69 Jul 28 '20 at 06:04
  • @enrico60 ok.. I think M2 is more commonly understood – grok_in_full Sep 02 '20 at 19:34
  • @RaphaelatDigitalPianism IS it possible => https://magento.stackexchange.com/questions/323412/set-password-link-magento-2 – Magento 2 Learner Sep 30 '20 at 17:11
  • @RakeshJesadiya bhai ji, Is it possible => https://magento.stackexchange.com/questions/323412/set-password-link-magento-2 – Magento 2 Learner Sep 30 '20 at 17:12

10 Answers10

117

You should not use the ObjectManager directly!

Exception from the rule are:

  • in static magic methods like __wakeup, serialize, etc
  • in case you should make backward compatibility of constructor
  • in global scope, like in fixtures of integration test.
  • in class that needs only for creation of object like factory, proxy , etc
Raphael at Digital Pianism
  • 70,385
  • 34
  • 188
  • 352
KAndy
  • 20,811
  • 3
  • 49
  • 59
  • 6
    I know I should never use it directly but why is Magento doing it ? ^^ – Raphael at Digital Pianism May 26 '16 at 11:41
  • 2
    in your example is for backward compatibility – KAndy May 26 '16 at 11:42
  • Are those always flagged as @deprecated ? – Raphael at Digital Pianism May 26 '16 at 11:54
  • 1
    What about this one: https://github.com/magento/magento2/blob/develop/app/code/Magento/GoogleOptimizer/Block/Adminhtml/Form.php#L57 ? – Raphael at Digital Pianism May 26 '16 at 12:02
  • Not allways, but its private, so we can remove its code at any time – KAndy May 26 '16 at 12:04
  • @RaphaelatDigitalPianism, it's hard to believe but Magento developing by humans and they can also make mistakes ;) – KAndy May 26 '16 at 20:00
  • 7
    oh yeah mate I know it is just confusing. Maybe they should have said "don't do it but be aware that we have probably left some mistakes here and there" ;) – Raphael at Digital Pianism May 26 '16 at 20:23
  • 1
    What if you have class of 10 functions and ONLY 1 function requires specific model? Wouldn't be redundant (from performance view) to load model through constructor injection for each of 10 functions while we could load it using object manager only inside 1 single function? – JohnyFree Jul 19 '16 at 15:38
  • 2
    if you have the class of 10 functions, you already have a problem and optional dependency, not a big deal. In any case, you may use proxy for optional dependencies – KAndy Jul 19 '16 at 16:03
  • @KAndy what if you want to use Custom Variables (Magento\Variable\Model\Variable) in phtml templates? Since a custom variable could potentially be used in any phtml file, it would be impossible to know ahead of time which core file to override for DI, so in this case what would be the best practice? – thdoan Oct 06 '16 at 02:43
  • 1
    what about injecting the object manager into a helper which can be used on any template? – minlare Dec 07 '16 at 11:18
  • 1
    Magento do not recommends use helpers, so... – KAndy Dec 07 '16 at 12:01
  • You might need to use object manager to implement refection. – Kervin Ramen May 12 '17 at 11:39
  • Another exception to this is soft dependencies. You can't DI the class in because it will throw an error if the class doesn't exist (which is totally fine if you have a soft dependency) – Nathan Merrill Nov 21 '17 at 21:15
  • @KAndy More than often Magento developing by humans make mistakes. Then you sometime have no other options, but use object manager to get around bugs. – newbie Jun 14 '18 at 08:45
  • @KAndy, What are your thoughts on using ObjectManager directly in php traits? It seems to me that its not really feasible to use constructor injection in traits as the constructor will almost always be overriden. E.g see my example in https://magento.stackexchange.com/questions/131210/magento-2-doesnt-support-dependency-injection-in-traits. – Andrew Kett Sep 20 '18 at 23:49
  • @AndrewKett, we do not recommend to use traits, and as result do not recommend use OM in traits – KAndy Sep 24 '18 at 05:56
  • 1
    You say we should not use it but don't give one single reason why. – Black Feb 26 '20 at 12:02
  • "You should not" does not answer the OP's question. – Alan Feb 16 '23 at 09:39
62

So why does M2 sometimes access object manager directly when we recommend against it?

Brutal answer: M2 is a port of M1 - not a complete rewrite. So don't assume that all the M2 code is perfectly ported yet (unfortunately). Just because you find something in the M2 code base, that does not mean "its the best way to do it". Sometimes it is just "we have not got around to fixing it yet".

Less brutal: As per other responses, sometimes you MUST use it as there is no alternative. Other times it might be for backwards compatibility reasons. And framework code sometimes makes sense using it directly, because it is framework code. But if I had to guess without looking at code, many really should be fixed but it has not been high enough priority to do so yet.

Just remember the good parenting advice: "Kids, do what I say, not what I do!"

7ochem
  • 7,532
  • 14
  • 51
  • 80
Alan Kent
  • 3,488
  • 13
  • 22
  • 13
    excellent quote:Kids, do what I say, not what I do! – sivakumar Jun 01 '16 at 01:16
  • 1
    That's not how it works kiddo – Ansyori Jun 01 '17 at 07:36
  • Is there a Magento 2 recommend way to have a soft dependency problem without object manager? I have a module with a soft dependency on another (it loads another class if the module exists). I can't DI that class in because then DI will fail. I can't even DI a Factory for that class because the factory will fail to DI. – Nathan Merrill Mar 14 '18 at 14:54
59

You should never use \Magento\Framework\App\ObjectManager::getInstance().
It defeats the purpose of dependency injection. We're back at Mage::getModel().
Object manager should be used only in factories and then as injected in a constructor.

The advantage of using this is less code to write. But this does not make it OK.
The fact that this is still used in the core, is because it didn't get refactored yet. I hope it will be.

Marius
  • 197,939
  • 53
  • 422
  • 830
  • 6
    So we both agree that Magento code is doing it wrong right ? – Raphael at Digital Pianism May 26 '16 at 11:41
  • 15
    right. they are wrong :). – Marius May 26 '16 at 11:43
  • I don't think they're using wrong. They're using it when necessary: when dynamic resolving is needed (plugins, especially) and when keeping BC on immediately-deprecated methods. – nevvermind May 26 '16 at 12:32
  • @nevvermind see the github link in my question, it definitely doesn't sound right to me to use OM in this case – Raphael at Digital Pianism May 26 '16 at 13:20
  • Maybe that particular bit should be refactored, but, in general, I don't see a problem directly using the OM to instantiate classes which you don't know beforehand (aka dynamic). How would you do that without using the OM? – nevvermind May 26 '16 at 14:56
  • 2
    @nevvermind Using a factory. You use di.xml to create a key => class name map and inject that map in to constructor of the factory and use the factory to instantiate the class through objectmanager – Marius May 26 '16 at 15:07
  • That's good for small maps, but when it gets bigger, you'll find that that array is getting harder to maintain and it's starting to actually duplicate an aspect of the OM: a map of classes/objects. It becomes a whitelist, filtering out possible valid cases. I dislike it when people use the OM directly for various things, too, but I'm also trying to be flexible. Asking M2 never to use the OM in their models is a bit rigid, in my opinion. – nevvermind May 26 '16 at 15:28
  • 2
    @nevvermind But a Magento employee's opinion outranks your opinion. You have an answer above from KAndy that states in bold letter "you should not use the object manager directly": http://magento.stackexchange.com/a/117103/146 I guess that kind of clears the fog on the issue. – Marius May 26 '16 at 15:31
  • Not sure about the criteria used in outranking someone else's opinion, but yeah, I guess so. ¯_(ツ)_/¯ LE: I think he's saying "Don't use eval in PHP". – nevvermind May 26 '16 at 15:34
  • there are various reasons why you should not use it directly. You can google it :P – David Verholen May 26 '16 at 17:40
  • I'm currently using \Magento\Framework\App\ObjectManager::getInstance() to output custom variables in phtml templates since Magento 2 doesn't seem to have a built-in method to do this. – thdoan Mar 08 '17 at 09:42
  • @10basetom. This is wrong. your templates should not contain logic. Move the logic part to a block class and use that one. – Marius Mar 08 '17 at 09:45
  • @Marius thanks for the advice. However, if I'm getting custom variables in multiple modules (e.g., Magento_Catalog, Magento_Checkout), would I have to override Block class for each module? – thdoan Mar 10 '17 at 01:50
  • @Marius, What if I want to use a custom table model in an event observer? How can I get the collection in an event? – Magento Learner Sep 12 '17 at 11:09
  • Thank you for your advice, just a quick query what be your advice on getting the names of my categories if I should never use \Magento\Framework\App\ObjectManager::getInstance() ? Thanks – John Feb 01 '18 at 08:49
  • @JohnC There are core classes that do this (not sure which ones). You should use in one of your blocks that class, inject it in the constructor and retrieve the names. Maybe you should ask a separate question about this. – Marius Feb 01 '18 at 08:54
  • Creation aka factories can use object manager, business logic (that should separated from the business logic - could use object manager, and never use the word : NEW in your classes - that's not testable)

    Also you can't really do certain patterns like composite, pools without object manager, which is all over Magento but it's on the "creation" part of things not interaction between entities

    – PartySoft Mar 14 '18 at 20:42
  • and to this day its still not refactored – Afzal Arshad Aug 25 '22 at 11:44
  • most probably it will not be because it would break the backwards compatibility. Maybe in a major version change... ? – Marius Aug 25 '22 at 12:41
24

Why is Magento doing what they recommend us not to do ? Does that mean there are some cases where we should use the ObjectManager directly ? If so, what those cases ?

Without knowing the full story here is my guess:

During the development of M2 the Magento team at some stage ran an automated script which replaced occurrences of Mage:getModel(), Mage::getSingleton(), $layout->createBlock(), etc. to use the ObjectManager.

Later refactoring should have fixed this to instead use proper dependency injection but there wasn't enough time / resources to convert all occurrences.

Also the Magento team lately seems to use this as an escape mechanism. Instead of breaking an existing implementation (by needing to change the constructor) they simply hide the new dependency via the ObjectManager. I can't say I agree with this approach - writing worse code to avoid a BC break.

What are the direct consequences of using the ObjectManager directly ?

I think your question already includes enough reasons. Generally it creates a hidden dependency, in other words the dependency is in the implementation details and not visible from the constructor alone.

Kristof at Fooman
  • 11,350
  • 28
  • 67
  • It is ironic because had there done it properly before releasing to the public the BC wouldn't have been an issue at all – scrowler May 31 '16 at 17:50
16

Should not use Object manager directly!

For instance:

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

also if you are working with event observers or plugins, you should never use it directly.

You could use it in Factories, but except that you should inject the Object Manager in the Constructor first then you can use its object in your method

Preferred to use:

1) declare private object:

private $_objectManager;

2) inject in the constructor and initialize:

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

3) use in some method:

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

This answer is for below Magento 2.2 versions, so please take a note. As per new Magento 2 standards now we can not use even objectManager instance, too. We have to use factory of the object class or repository to get any data.

Ronak Chauhan
  • 6,183
  • 2
  • 28
  • 65
  • Is it a good practice to use it this way? – enrico69 Sep 29 '17 at 16:04
  • Yes, because magento doesn't allow to use direct objectManager, so you have to use this way! – Ronak Chauhan Sep 29 '17 at 17:23
  • You should also never use it in events (I guess you mean Observers) and plugins. You should inject the objects that you need, not the ObjectManager. Only in a Factory you could use the ObjectManager and then you should indeed inject it instead of calling ::getInstance() – 7ochem Apr 10 '18 at 13:38
  • Right, edit the answer @7ochem – Ronak Chauhan Apr 16 '18 at 11:51
  • downvote any answer is not an appropriate way, If you have better knowledge then you can add your own answer or you can edit any other's answer to get better idea and helpful to others. @7ochem – Ronak Chauhan Apr 16 '18 at 11:57
  • Comment and downvote IS the proper way. I'll edit someone's answer to improve formatting and language/grammar. Changing the actual answer (code or explanation) is violation of the answer to me. But now that I have your consent, I will edit it and upvote again ;) – 7ochem Apr 16 '18 at 13:21
  • I am using same way for creating rest API's is it OK??? – Mahendra Aug 26 '19 at 07:10
  • for new version of magento 2, now we can not use even this way. you have to use repository to get data or create object instance directly with factory. @55840 – Ronak Chauhan Sep 02 '19 at 13:38
14

The main reason that developers are strongly discouraged from using the Object Manager directly is that direct use of the Object Manager causes the extension not to be installable in compiled production mode.

So it breaks for your customers using production mode, including all customers on Magento Cloud.

It seems like a reasonably large proportion of developers (approximately 75%) do not test their extensions to see if they can be installed in production mode, so do not run into the issues posed by incorrect ObjectManager usage.

As of 2017, the Magento Marketplace runs a compile and install test on all extensions sold through it. If your extension uses the Object Manager directly, it will fail these tests and be rejected from the Marketplace until you resolve this problem and reupload.

Black
  • 3,310
  • 4
  • 31
  • 110
Dewi Morgan
  • 363
  • 3
  • 8
  • What is release mode? Do you mean production mode? – Black Apr 27 '21 at 13:56
  • @Black Is that what the cool kids are calling it? It's been years since I worked for Magento, or cared about it. Call it what you want, the important part of this answer as I remember it was "compiled". Direct use of the OM breaks compilation. – Dewi Morgan Apr 28 '21 at 17:19
3

You can try by creating an object of objectManager and should not use objectManager directly.

Use something like,

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
  • 2,981
  • 3
  • 20
  • 42
3

You should not call the object manager directly because the framework handles this automatically. You can see few exceptions that Magento 2 core classes call the ObjectManager directly because this code needs exist for backward compatibility purposes. But, they are not approval of using the ObjectManager directly.

Moreover, if you run:

bin/magento dev:di:info "Magento\Quote\Model\QuoteRepository"

you will get "information on Dependency Injection configuration", but you will not get the information of direct usage of ObjectManager. Also, direct use of its create() function prevents type validation and type hinting that a factory class provides.

Wasim
  • 445
  • 4
  • 17
0

As of year 2021:

The Magento framework uses the ObjectManager to generate and inject the classes declared in your constructor. Classes should not ask for the ObjectManager itself as a constructor dependency.

You do not call the object manager directly because the framework handles this automatically. Direct use of the create function prevents type validation and type hinting that a factory class provides.

Source

sv3n
  • 11,657
  • 7
  • 40
  • 73
0

Magento addresses these questions in their developer documentation.

Source: https://devdocs.magento.com/guides/v2.4/extension-dev-guide/object-manager.html#usage-rules


Question 1

Why is Magento doing what they recommend us not to do ? Does that mean there are some cases where we should use the ObjectManager directly? If so, what are those cases?

Magento Developer Documentation Answer:

You may notice in the Magento 2 codebase that some core classes still call the ObjectManager directly. This code needs porting or exist for backward compatibility purposes. They are not tacit endorsements of using the ObjectManager directly.


Question 2

What are the consequences of using the ObjectManager directly?

Magento Developer Documentation Answer:

Direct use of the create function prevents type validation and type hinting that a factory class provides.

PromInc
  • 460
  • 3
  • 7