2

I know I can achieve this with object Manager - but just wanted to see if there is a better way. Perhaps this case is a good candidate for using the object Manager; however, I would like to do things properly so it'd be good to get some feedback from more experienced developers.

I want to inject an object via a class constructor - and then within the body of the constructor decide what type of object it is.

I will use the Module Manager to check if a module exists - if so then I'd like to use one of its classes. Otherwise, I'd like to use another class in core.

The reason it is possible is because both the classes (the one that might not be available and the one in the core) have the same class name and method that I need to use).

Obviously I can't inject the class that might not exist directly. Here is some code as an example:

/**
 * MyClass constructor.
 *
 * @param {unsure how to define it} $object
 * @param \Magento\Framework\Module\Manager $moduleManager
 */
public function __construct({unsure how to define it} $object,
                            \Magento\Framework\Module\Manager $moduleManager,
)
{
    $this->_moduleManager = $moduleManager;

    if ($this->_moduleManager->isEnabled('ThirdPartyExtension_PerhapsNotInstalled')) {
        $object =  $classThatMightNotExist;
    } else {
        $object = $coreClass;
    }

    $this->_object = $object;
}

2 Answers 2

11

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
    ) {
        $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.

6
  • Looks like a good solution. +1. May 8, 2017 at 13:27
  • @marius - Thanks, definitely getting closer - only thing is I might have confused the situation by saying 'myExtensionPerhapsNotInstalled' - it's actually a third part extension that might not be installed. It resembles a core class, has similar functionality, but does not override the core. So they're separate - but with the same methods, classes, etc. Sorry for misunderstanding
    – BAF
    May 8, 2017 at 13:28
  • @BAF. I see. In this case you have a dependency problem. I will edit the answer with a solution that might work in your case also.
    – Marius
    May 8, 2017 at 13:34
  • @BAF. See my edit (second approach). I didn't test, but I think it should work.
    – Marius
    May 8, 2017 at 13:45
  • @marius - makes sense, thanks for this it has been very helpful
    – BAF
    May 8, 2017 at 13:48
0

I would suggest using instanceof. You can check if the class is available like this. A class would be available if the module is enabled only. So

if ($pagerBlock instanceof \Magento\Framework\DataObject) {

And example implication can be something like this. // Taken from this.

if ( ($obj instanceof MyClass) != true ) {
    $obj = new MyClass();
}

No, i wont try to DI it. Just a speculation. Suggestions are welcome !!

Also a core php type solution can be like this.

3
  • Thanks @Kingshuk, I understand - although would you load $pagerBlock in the first place using DI if \Magento\Framework\DataObject didn't exist? You would get a compilation error, so what would the type be to load in $object?
    – BAF
    May 8, 2017 at 12:50
  • True. I wont try to use DI to make it. May 8, 2017 at 13:08
  • Thanks, I think instead of going through all of the extra steps to instantiate the classes, probably object manager can be used in this case?
    – BAF
    May 8, 2017 at 13:15

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.