Использование AbstractPluginManager с Zend Expressive 3

Я хотел бы добавить массив объектов, которые реализуют общий интерфейс, в один из моих сервисов. Я использую Zend ServiceManager в качестве контейнера DI. Я уже довольно давно читаю документы, и мне кажется, что AbstractPluginManager - это то, что нужно. Я не смог заставить его работать, хотя. Есть ли пример использования AbstractPluginManager + Zend Expressive 3, на который я могу взглянуть?

Моя конечная цель - динамически внедрить в мой сервис все зарегистрированные классы, которые реализуют общий интерфейс.

Пример:

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

MyService

__construct(array Iimplementations){...}

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

$service has Iimplementations

заранее спасибо

2 ответа

AbstractPluginManager в основном предназначен для проверки и фильтрации плагинов. Вы можете создавать классы, и во время проверки вы можете передать конкретную конфигурацию, которая делает фильтр или валидатор многократно используемым.

То, что вы ищете, это, вероятно, абстрактная фабрика. Вы регистрируете фабрику один раз, и она может создать для вас сервис. В вашем случае с конкретным набором зависимостей.

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,
        ],
    ],
];

Вы также можете сходить с ума и использовать отражение, чтобы обнаружить зависимости, если они различны для каждого класса, однако это добавляет много накладных расходов. Я думаю, что проще и удобнее создавать отдельные фабрики. И затем есть Zend-Expressive-Tooling, который является инструментом Cli, который может создавать фабрики, обработчики и промежуточное программное обеспечение.

/*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);
    }
 }
Другие вопросы по тегам