0

I have a simple interface, PurgerInterface that has the methods needed to purge items from a cache backend (Varnish or Fastly). I have some custom configuration (standard system.xml stuff) that allows an admin to decide which backend they'd like to use (Varnish or Fastly).

I also have an Observer that listens to various events and will purge the appropriate caches. Here is the issue: I would like to inject, in my observer constructor, this purger interface like so:

public function __construct( CacheConfig $config, \Psr\Log\LoggerInterface $logger, PurgerInterface $purger) { ....

Now, the issue is that the concrete implementation of the PurgerInterface that I'd like injected is dependent on a configuration. If the Varnish backend is selected, I'd like VarnishPurger injected. If Fastly is selected, I'd like FastlyPurger.

Now, I'm familiar with other frameworks where you are able to dynamically bind items in the "service container" (I'm thinking Laravel here). Basically the logic / code controlling dependency injection can be interacted with, so when the container asks for a PurgerInterface I can run some code to check which concrete class I should return, and that is the class that is injected.

So, is this possible?

2

First, you need to create the factory that will be responsible for creating needed class base on configuration values. like

class Factory 
{
  ....
    public function create($data)
    {
        $className = $this->getClassFromConfiguration();
        $mapperInstance = $this->_objectManager->create($className, $data);

        if (false == $mapperInstance instanceof YourAdapterInterface) {
            throw new \Exception(
                'Object is not instance on YourAdapterInterface'
            );
        }
        return $mapperInstance;
    }
}

If you need to implements this for shared instance you should create Proxy.

class Proxy implements \Magento\Framework\ObjectManager\NoninterceptableInterface
{

    /**
     * @var \Magento\Config\Model\Config\Structure\Element\Group
     */
    protected $_subject;

    /**
     * @param Factory $factory
     */
    public function __construct($factory)
    {
        $this->factory = $objectManger;
    }

    /**
     * Retrieve subject
     *
     * @return YourAdapterInterface
     */
    protected function _getSubject()
    {
        if (!$this->_subject) {
            $this->_subject = $this->factory->create()
        }
        return $this->_subject;
    }

    /**
     * Method from interface
     *
     */
    public function methodName()
    {
        return $this->_getSubject()->methodName();
    }

    ... implementation of all methods from the interface by pattern

}

and use this class as the preference for interface

  • Thank you very much for the reply @KAndy! Can you explain the idea / logic / code behind the proxy as it related to a "shared instance"? The factory idea is a good one and ultimately if this is the way to go about creating my instance based on configuration then that's fine, although I had hoped that Magento 2 would allow for a way to do this via some built in DI feature. – Kevin Mitchell Jul 5 '16 at 13:45
  • @KevinMitchell Proxy is referring as a lazy loading pattern for the dependencies, so you can load the dependency but is like switched off until you use the first time in the main class. Shared refers to singleton pattern so can't be instantiated more than once. – Elio Ermini Sep 14 '16 at 10:20
  • as per magento we should not use _objectManager , with out _objectManager can we achieve this? if yes please how to – Pradeep Kumar Oct 27 '17 at 10:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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