nbo*_*iot 5 php dependency-injection symfony
我正在开发一个应用程序,其中有一些处理程序作为我希望能够调用的服务。他们都实现了一个ItemHandlerInterface.
我希望能够在控制器中检索所有ItemHandlerInterface服务集合,而无需手动连接它们。
到目前为止,我具体标记了它们:
服务.yaml
_instanceof:
App\Model\ItemHandlerInterface:
tags: [!php/const App\DependencyInjection\ItemHandlersCompilerPass::ITEM_HANDLER_TAG]
lazy: true
Run Code Online (Sandbox Code Playgroud)
并尝试在控制器中检索我的服务集合。如果只有一个服务实现,它就可以工作ItemHandlerInterface,但是一旦我创建了多个服务(如下面的TestHandler和 )Test2Handler,我最终会得到一个The service "service_locator.03wqafw.App\Controller\ItemUpdateController" has a dependency on a non-existent service "App\Model\ItemHandlerInterface".
如何动态检索实现我的接口的所有服务?
一种肮脏的解决方案是强制所有ItemHandlerInterface内容public: true并传递Container给我的控制器构造函数。但这很丑陋,我想找到一种更优雅的方式。
项目更新控制器
namespace App\Controller;
use App\Model\ItemHandlerInterface;
use App\Service\ItemFinder;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Debug\Exception\ClassNotFoundException;
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use App\Model\Item;
use Psr\Container\ContainerInterface;
/**
* Class ItemUpdateController
*
* @package App\Controller
*/
class ItemUpdateController extends AbstractController
{
/**
* @var ContainerInterface
*/
protected $locator;
public function __construct(ContainerInterface $locator)
{
$this->locator = $locator;
}
public static function getSubscribedServices()
{
// Try to subscribe to all ItemHandlerInterface services
return array_merge(
parent::getSubscribedServices(),
['item_handler' => ItemHandlerInterface::class]
);
}
/**
* @param string $id
* @param RequestStack $requestStack
* @param ItemFinder $itemFinder
*
* @return Item
* @throws \Symfony\Component\Debug\Exception\ClassNotFoundException
*/
public function __invoke(
string $id,
RequestStack $requestStack,
ItemFinder $itemFinder
) {
// Find item
$item = $itemFinder->findById($id);
// Extract and create handler instance
$handlerName = $item->getHandlerName();
if($this->locator->has($handlerName)) {
$handler = $this->locator->get($handlerName);
$request = $requestStack->getCurrentRequest();
$payload = json_decode($request->getContent());
call_user_func($handler, $payload, $request);
return $item;
}
}
}
Run Code Online (Sandbox Code Playgroud)
src/ItemHandler/TestHandler.php
namespace App\ItemHandler;
use App\Model\ItemHandlerInterface;
use Doctrine\ORM\EntityManagerInterface;
class TestHandler implements ItemHandlerInterface
{
// implementation
}
Run Code Online (Sandbox Code Playgroud)
src/ItemHandler/Test2Handler.php
namespace App\ItemHandler;
use App\Model\ItemHandlerInterface;
use Doctrine\ORM\EntityManagerInterface;
class Test2Handler implements ItemHandlerInterface
{
// implementation
}
Run Code Online (Sandbox Code Playgroud)
您可以一次性注入所有标记的服务,而无需使用编译器通道。
由于您已经在进行标记,如问题所示,因此只需声明注入即可:
_instanceof:
App\Model\ItemHandlerInterface:
tags: ['item_handler']
lazy: true
services:
App\Controller\ItemUpdateController:
arguments: !tagged 'item_handler'
Run Code Online (Sandbox Code Playgroud)
您需要更改控制器的构造函数,以便它接受iterable:
_instanceof:
App\Model\ItemHandlerInterface:
tags: ['item_handler']
lazy: true
services:
App\Controller\ItemUpdateController:
arguments: !tagged 'item_handler'
Run Code Online (Sandbox Code Playgroud)
在你的类aRewindableGenerator里面会注入你的服务。您可以简单地迭代它来获取其中的每一个。
该功能自 3.4 起可用;并且仍然支持。
从 4.3 开始,您可以使用标记服务定位器来实现此目的。配置同样简单,但您的优势在于能够延迟实例化服务,而不必首先实例化所有服务。
你可以在这里阅读更多。