简体   繁体   中英

Using AbstractPluginManager with Zend Expressive 3

I'd like to inject an array of objects that implement a common interface into one of my services. I am using zend servicemanager as the DI container. I have been reading the docs for quite a bit now and it seems to me that AbstractPluginManager is the way to go. I haven't been able to make it work though. Is there an example using an AbstractPluginManager + Zend Expressive 3 that I can take a look at?

My ultimate goal is to dynamically inject all registered classes that implement a common interface into my service.

Example:

interface I{}
class A implements I{}
class B implements I{}
class C{}

MyService

__construct(array Iimplementations){...}

$service = $container->get('myservice')

$service has Iimplementations

Thanks in advance

The AbstractPluginManager is mostly for validation and filter plugins. You can create classes and while validating, you can pass specific configuration which makes the filter or validator re-usable.

What you are looking for is probably an abstract factory . You register the factory once and it can create a service for you. In your case with a specific set of dependencies.

interface I{}
class A implements I{}
class B implements I{}

class MyAbstractFactory implements AbstractFactoryInterface
{
    public function canCreate(ContainerInterface $container, $requestedName)
    {
        return in_array('I', class_implements($requestedName), true);
    }

    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        return new $requestedName(
            $container->get(DependencyFoo::class),
            $container->get(DependencyBar::class)
        );
    }
}

// config/autoload/dependencies.global.php
return [
    'dependencies' => [
        'factories' => [
            // ...
        ],

        'abstract_factories' => [
            MyAbstractFactory::class,
        ],
    ],
];

You can also go crazy and use reflection to detect dependencies if they are different for each class, however that adds a lot of overhead. I think it's easier and more maintainable to create separate factories. And then there is zend-expressive-tooling which is a cli tool that can create factories, handlers and middleware.

/*Getting I concrete implementations via the plugin manager will ensure the implementation of the I interface*/   
class IPluginManager extends AbstractPluginManager
{
   protected $instanceOf = I::class;

   public function getIConcreteImplementations()
   {
       $concreteImpl = [];
       foreach(array_keys($this->factories) as $key)
       {
           $concreteImpl[] = $this->get($key);
       }

       return $concreteImpl;
    }
}
/*IPluginManagerFactory*/
class TransactionSourcePluginManagerFactory
{
  const CONFIG_KEY = 'i-implementations-config-key';

public function __invoke(ContainerInterface $container, $name, array $options = null)
{
    $pluginManager = new IPluginManager($container, $options ?: []);

    // If this is in a zend-mvc application, the ServiceListener will inject
    // merged configuration during bootstrap.
    if ($container->has('ServiceListener')) {
        return $pluginManager;
    }

    // If we do not have a config service, nothing more to do
    if (! $container->has('config')) {
        return $pluginManager;
    }

    $config = $container->get('config');

    // If we do not have validators configuration, nothing more to do
    if (! isset($config[self::CONFIG_KEY]) || ! 
      is_array($config[self::CONFIG_KEY])) {
        return $pluginManager;
    }

    // Wire service configuration for validators
    (new Config($config[self::CONFIG_KEY]))->configureServiceManager($pluginManager);

    return $pluginManager;
  }
}
/*In the ConfigProvider of the module or global config*/
class ConfigProvider
{
/**
 * Returns the configuration array
 *
 * To add a bit of a structure, each section is defined in a separate
 * method which returns an array with its configuration.
 *
 */
   public function __invoke() : array
   {
    return [
        'dependencies' => $this->getDependencies(),
        'routes' => $this->getRoutes(),
        'i-implementations-config-key' => $this->getIConcreteImplementations(),
    ];
   }
   public function getIConcreteImplementations() : array
   {
    return [
        'factories' => [
            A::class => AFactory::class,
            B::class => InvokableFactory::class,
        ],
    ];
   }
}
/*I can now be sure that I am injecting an array of I implementations into my Service*/
class ServiceFactory
{
    public function __invoke(ContainerInterface $container) : Service
    {
        $pluginManager = $container->get(IPluginManager::class);

        $impl = $pluginManager->getIConcreteImplementations();

        return new Service($impl);
    }
 }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM