12

When I think I got my head wrapped around the DI system from Magento 2 something comes up and un-wraps it.
I see in the core code different ways to access a helper.
For example in Magento\Catalog\Controller\Category::_initCategory there is this:

if (!$this->_objectManager->get('Magento\Catalog\Helper\Category')->canShow($category)) {
    return false;
}

But in Magento\Catalog\Block\Category\View the helper is injected int he constructor

public function __construct(
    \Magento\Framework\View\Element\Template\Context $context,
    \Magento\Catalog\Model\Layer\Category $catalogLayer,
    \Magento\Framework\Registry $registry,
    \Magento\Catalog\Helper\Category $categoryHelper,
    array $data = array()
) {
    $this->_categoryHelper = $categoryHelper;
    $this->_catalogLayer = $catalogLayer;
    $this->_coreRegistry = $registry;
    parent::__construct($context, $data);
}

This lead me to think that the helpers should be accessed differently in controllers and blocks (and models) but then I found a controller where a helper is injected in the constructor Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute.

Please clear the fog for me.
When should I use DI and when should I use objectManager? and why?
I've read this question: Instantiating Helpers in Magento 2. This is just a follow up question on that.

4 Answers 4

11

I would prefer DI where possible, as using the object manager is already a violation against the law of demeter. When using the object manager these dependencies are just hidden in the method logic.

3
  • Yep. I agree. I will use DI, but I'm curious to why this is done in the core? Maybe someone did not get to refactor the classes I mentioned, yet?
    – Marius
    Jul 11, 2014 at 9:27
  • Afaik they're still refactoring a lot and hope they'll also touch these places, too. But also don't know about the priorities, which have to exist if they want to actually release at some point. So maybe some new features or other bad practices will be fixed first.
    – Tobias
    Jul 11, 2014 at 9:55
  • 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, 2016 at 15:40
6

I don't know so much about the Magento implementation, but it looks like the ObjectManager is a Service Locator.

Generally using a Service Locator to access dependencies in an object is pretty bad, checkout out this article.

Explicitly defining your dependencies through a constructor is a much better approach. It aid's in unit testing and run time issues with services not being defined.

Injecting the Object Manager in to a class is basically injecting a Registry in to your class which has access to all of your applications services, which is obviously not right.

I use ZF2 a fair bit and generally define small factory classes for Services, Controllers and any class which require dependencies. These factory classes have access to the Service Locator and grab all services which the object depends on, and injects them through the constructor. Using a Service Locator in a Factory class is fine as it is mostly throw away code, something like this for example.

These factories are still easy to test.

IMO, Use constructor injection where ever possible. Again, I don't know too much about Magento's implementation and if it has the concept of Factories, from a quick look it does look like it supports them, but explicitly defining your classes and using a Service Locator to build them in Factory classes is a much cleaner approach.

This is from someone who has limited exposure to the above mentioned patters, so I would also like to hear other's thoughts / experiences on the matter!

More reading

2
  • Thanks for the nice explanation. My question was "Why are there 2 ways to access a helper in the core?" so this is a bit off-topic but it cleared some other doubts I had. :) Thanks.
    – Marius
    Jul 11, 2014 at 9:26
  • 1
    I would probably say that it's just something that's just not refactored yet. Either that or it might be an ease-of-use thing. Requiring consumers to always inject all their dependencies in a Controller could be seen as counter-productive, especially when performing RAD. Giving consumers both ways to access dependencies would allow for the RAD approach but still allow for others to explicitly define their dependencies if they so wish. Jul 11, 2014 at 9:33
5

One another way to use helper (in templates) is:

$this->helper('[Vendor]\[Module]\Helper\[Helper Name]')->getMethodName();

I hope it is useful if you didn't already know.

4
  • 1
    this is somehow similar to using the object manager. Not sure it's the best idea.
    – Marius
    Apr 19, 2016 at 17:29
  • 1
    Above method is only for templates as far as I know. Object manager is used in controllers, blocks, models etc.
    – rbncha
    May 12, 2016 at 15:06
  • 1
    It's not in the same ballpark as code because there are no code dependencies on templates. The templates are only consumers and don't pollute any clients with broken encapsulation.
    – demonkoryu
    Feb 21, 2017 at 13:34
  • 1
    I don't know what demonkoryu is trying to say. But The best way to call any module's helper is this. This is Magento. As they say, every block/section code is intended to be callable/modifiable without touching core. So, everything is interrelated or have dependencies.
    – rbncha
    Jan 7, 2018 at 5:03
2

Though it is old question, I’m not sure whether Marius got its answer. I believe Marius can answer it better. I would like to answer it in short. Why Magento 2 suggests to use DI instead of helper?

  • Making isolation in unit testing possible/easy
  • Explicitly defining dependencies of a class
  • Facilitating good design (single responsibility principle (SRP) for example)
  • Using DI in your module reduces the risk of incompatibility bugs when Magento changes the underlying implementation of those interfaces. This is an important concept to understand for extension developers.

Why M2 core might not use DI in some cases?

  • Decreasing number of classes
  • Not creating of unnecessary interfaces
  • No risk of incompatibility bugs

Though Core catalog module has been used helper, it has used DI extensively. In my research, I found Magento 2 used few functions in Core Catalog helper files which are not suitable for Service Contracts.

If you must explicitly use a Magento-defined class ( such as \Magento\Catalog\Model\Product), make the implicit dependency explicit by depending on the concrete implementation instead of the service contract interface.

Undoubtedly, extension developer should use DI instead of Magento1 like Helper. When implementing according to Magento 2’s guidelines, the fallout is limited. When breaking recommendations, problems happen.

1
  • Yeah, I got my answer in the meantime. But thanks for taking the time to answer. This is valuable information for people searching for this online.
    – Marius
    Oct 2, 2019 at 14:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.