Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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

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.

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top