Question

We are developing a Magento 2 extension and we need to inject a class from a third party extension that the user may or may not have installed.

We thought about injecting the class via the di.xml file and set as an optional parameter in the class constructor.

When we run 'bin/magento setup:di:compile' no errors are thrown, however when refreshing the pages, a class not found exception is thrown.

How can we check that the class exists before injecting in the di.xml?

I have something like this in my di.xml:

    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="My\Custom\Class\Builder">
        <arguments>
            <argument name="thirdPartyHelper" xsi:type="object">Third\Party\Helper</argument>
        </arguments>
    </type>
</config>

And something like this in the class that i'm trying to inject:

    class Builder
    {
        private $dependency1;
        private $thidPartyHelper;

        public function __construct(
            Dependency1 $dependency1,
            ThidPartyHelper $thidPartyHelper = null
        ) {
            $this->dependency1 = $dependency1;
            $this->thidPartyHelper = $thidPartyHelper;
        }
    }
Was it helpful?

Solution

First Approach:
The cleanest way is to not take decisions in the constructor.
Let the di.xml do that for you.
Let's say the core class you are using if a module is not present is Some\Core\ClassName.
And the class that should use this core class us My\Custom\ClassName.

And the class that might not exist is MyExtension\PerhapsNotInstalled\ClassThatMightNotExist

Make your class constructor like this:

public function __construct(
    \Some\Core\ClassName $object 
) {
    $this->_object = $object;
}

Now, in your module MyExtension_PerhapsNotInstalled di.xml file add this:

<type name="My\Custom\ClassName">
    <arguments>
        <argument name="object" xsi:type="object">MyExtension\PerhapsNotInstalled\ClassThatMightNotExist</argument>
    </arguments>
</type>

just make sure that MyExtension\PerhapsNotInstalled\ClassThatMightNotExist extends the core class Some\Core\ClassName.

This way, if the module MyExtension_PerhapsNotInstalled is not installed, the original core class Some\Core\ClassName will be used, otherwise MyExtension\PerhapsNotInstalled\ClassThatMightNotExist will be used.

Second Approach:
If your extension that might not exist, is a third party extension that you cannot control, and you want your code to work with it and without it, you can do this (using the same notations as above):

Create your own class that acts as a factory:
Let's call it My\Module\Factory

<?php 

namespace My\Module;
class Factory
{
    protected $moduleManager;
    protected $objectManager;
    public function __construct(
        \Magento\Framework\Module\Manager $moduleManager,
        \Magento\Framework\ObjectManagerInterface $objectManager $objectManager
    ) {
        $this->moduleManager = $moduleManager;
        $this->objectManager = $objectManager;
    }
    public function create(array $data = array())
    {
        if ($this->_moduleManager->isEnabled('ThirdPartyExtension_PerhapsNotInstalled')) {
            $instanceName =  'MyExtension\PerhapsNotInstalled\ClassThatMightNotExist';
        } else {
            $instanceName = 'Some\Core\ClassName';
        }
        return $this->objectManager->create($instanceName, $data);
    }
}

You should inject this new class in the constructor of your My\Custom\ClassName class and call the create method from it to determine the class instance you need.

public function __construct(
    \My\Module\Factory $factory 
) {
    $this->_object = $factory->create();
}

Note: This second approach is not really clean, because in theory your dependencies should be explicit and the composition should be done via di.xml but I certainly understand the need.
Second Note: You are not violating any guidelines of not using the object manager, because OM is allowed in factories and you just created a factory.
And your class does not depend on the OM directly.
I didn't test the code, so watch out for syntax errors.

Ref : Magento2: Checking if a module exists, after being injected via class constructor

OTHER TIPS

You could inject a factory in the constructor that returns the optional object when it's available and a null object otherwise.

You could use ModuleList to see if the class/module is available.

I needed a quick way to do this for a private script, without messing around with factories and other nonsense.

You can just check in the constructor:

public function __construct(
    \Magento\Framework\Module\Manager            $moduleManager,
    \Magento\Framework\ObjectManagerInterface    $objectManager
) {
    $this->moduleManager = $moduleManager;
    $this->objectManager = $objectManager;

    if ($moduleManager->isEnabled("MyOptionalModule_MyModule")) {
        $this->optionalModule = $objectManager->get("\My\Optional\Module");
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top